diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c0a6e73e0..96b9caf3c 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,7 +17,8 @@ jobs: steps: - name: Publish on Winget - uses: vedantmgoyal2009/winget-releaser@v2 + # @Octol1ttle: Pinned to a commit because no release has been published that works on ubuntu-slim + uses: vedantmgoyal2009/winget-releaser@7bd472be23763def6e16bd06cc8b1cdfab0e2fd5 # docs: add description to inputs (#335) with: identifier: FreesTeam.FreesmLauncher version: ${{ github.event.release.tag_name }} diff --git a/launcher/Application.cpp b/launcher/Application.cpp index ac6ce4c79..fda21d2bf 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -924,8 +924,10 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) } { auto resetIfInvalid = [this](const Setting* setting) { - if (const QUrl url(setting->get().toString()); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) { - m_settings->reset(setting->id()); + if (const auto value = setting->get().toString(); !value.isEmpty()) { + if (const QUrl url(value); !url.isValid() || (url.scheme() != "http" && url.scheme() != "https")) { + m_settings->reset(setting->id()); + } } }; @@ -948,17 +950,9 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting("MSAClientIDOverride", ""); // Custom Flame API Key - { - m_settings->registerSetting("CFKeyOverride", ""); - m_settings->registerSetting("FlameKeyOverride", ""); - - QString flameKey = m_settings->get("CFKeyOverride").toString(); - - if (!flameKey.isEmpty()) - m_settings->set("FlameKeyOverride", flameKey); - m_settings->reset("CFKeyOverride"); - } + m_settings->registerSetting({ "FlameKeyOverride", "CFKeyOverride" }, ""); m_settings->registerSetting("FlameKeyShouldBeFetchedOnStartup", true); + m_settings->registerSetting("FallbackMRBlockedMods", true); m_settings->registerSetting("ModrinthToken", ""); m_settings->registerSetting("UserAgentOverride", ""); diff --git a/launcher/BaseInstance.cpp b/launcher/BaseInstance.cpp index 92a38d78a..1a3972de2 100644 --- a/launcher/BaseInstance.cpp +++ b/launcher/BaseInstance.cpp @@ -42,6 +42,7 @@ #include #include #include +#include #include "Application.h" #include "Json.h" @@ -89,6 +90,14 @@ BaseInstance::BaseInstance(SettingsObject* globalSettings, std::unique_ptrregisterSetting("linkedInstances", "[]"); m_settings->registerSetting("shortcuts", QString()); + m_settings->registerSetting("uuid", QString()); + + const auto savedUUID = m_settings->get("uuid").toString(); + if (savedUUID.isEmpty()) { + regenerateUuid(); + } else { + m_uuid = savedUUID; + } // Game time override auto gameTimeOverride = m_settings->registerSetting("OverrideGameTime", false); @@ -272,6 +281,13 @@ QString BaseInstance::id() const return QFileInfo(instanceRoot()).fileName(); } +void BaseInstance::regenerateUuid() +{ + const auto newUUID = QUuid::createUuid().toString(QUuid::Id128); + m_settings->set("uuid", newUUID); + m_uuid = newUUID; +} + bool BaseInstance::isRunning() const { return m_isRunning; @@ -449,7 +465,7 @@ QList BaseInstance::shortcuts() const QString shortcutName = dict["name"].toString(); QString filePath = dict["filePath"].toString(); - if (!QDir(filePath).exists()) { + if (!QFileInfo::exists(filePath)) { qWarning() << "Shortcut" << shortcutName << "for instance" << name() << "have non-existent path" << filePath; continue; } diff --git a/launcher/BaseInstance.h b/launcher/BaseInstance.h index 3513b0240..c7c2c7d29 100644 --- a/launcher/BaseInstance.h +++ b/launcher/BaseInstance.h @@ -114,7 +114,9 @@ class BaseInstance : public QObject { /// The instance's ID. The ID SHALL be determined by LAUNCHER internally. The ID IS guaranteed to /// be unique. - virtual QString id() const; + QString id() const; + QString uuid() const { return m_uuid; } + void regenerateUuid(); void setMinecraftRunning(bool running); void setRunning(bool running); @@ -317,6 +319,7 @@ class BaseInstance : public QObject { RuntimeContext m_runtimeContext; private: /* data */ + QString m_uuid; Status m_status = Status::Present; bool m_crashed = false; bool m_hasUpdate = false; diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index 02e1529d4..e380f431e 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -1163,6 +1163,8 @@ SET(LAUNCHER_SOURCES ui/dialogs/ChooseOfflineNameDialog.h ui/dialogs/ModToggleConfirmDialog.cpp ui/dialogs/ModToggleConfirmDialog.h + ui/dialogs/UntrustedModsDialog.cpp + ui/dialogs/UntrustedModsDialog.h ui/dialogs/skins/SkinManageDialog.cpp ui/dialogs/skins/SkinManageDialog.h @@ -1337,6 +1339,7 @@ qt_wrap_ui(LAUNCHER_UI ui/dialogs/skins/SkinManageDialog.ui ui/dialogs/ChooseOfflineNameDialog.ui ui/dialogs/ModToggleConfirmDialog.ui + ui/dialogs/UntrustedModsDialog.ui ) qt_wrap_ui(PRISM_UPDATE_UI diff --git a/launcher/HardwareInfo.cpp b/launcher/HardwareInfo.cpp index 36b6f7783..44941949f 100644 --- a/launcher/HardwareInfo.cpp +++ b/launcher/HardwareInfo.cpp @@ -21,12 +21,14 @@ #include #include -#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) +#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD) || defined(Q_OS_OPENBSD) namespace { +#if defined(Q_OS_MACOS) || defined(Q_OS_LINUX) QString afterColon(QString str) { return str.remove(0, str.indexOf(':') + 2).trimmed(); } +#endif template bool readFromOutput(const char* command, F function) diff --git a/launcher/InstanceCopyTask.cpp b/launcher/InstanceCopyTask.cpp index e32cdf095..802b548ef 100644 --- a/launcher/InstanceCopyTask.cpp +++ b/launcher/InstanceCopyTask.cpp @@ -151,6 +151,7 @@ void InstanceCopyTask::copyFinished() BaseInstance* inst(new NullInstance(m_globalSettings, std::move(instanceSettings), m_stagingPath)); inst->setName(name()); inst->setIconKey(m_instIcon); + inst->regenerateUuid(); if (!m_keepPlaytime) { inst->resetTimePlayed(); } diff --git a/launcher/InstanceDirUpdate.cpp b/launcher/InstanceDirUpdate.cpp index 75fbdb6c6..bea4f1070 100644 --- a/launcher/InstanceDirUpdate.cpp +++ b/launcher/InstanceDirUpdate.cpp @@ -66,6 +66,13 @@ QString askToUpdateInstanceDirName(BaseInstance* instance, const QString& oldNam return QString(); } + if (instance->isRunning()) { + QMessageBox::warning(parent, QObject::tr("Cannot rename instance folder"), + QObject::tr("The instance folder cannot be renamed while the instance is running.\n\n" + "Only the instance name will be changed. The folder will keep its current name.")); + return QString(); + } + // Ask if we should rename if (renamingMode == "AskEverytime") { auto checkBox = new QCheckBox(QObject::tr("&Remember my choice"), parent); diff --git a/launcher/InstanceImportTask.cpp b/launcher/InstanceImportTask.cpp index 9b04f99b6..e7c251168 100644 --- a/launcher/InstanceImportTask.cpp +++ b/launcher/InstanceImportTask.cpp @@ -59,8 +59,8 @@ #include #include -InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, QWidget* parent, QMap&& extra_info) - : m_sourceUrl(sourceUrl), m_extra_info(extra_info), m_parent(parent) +InstanceImportTask::InstanceImportTask(const QUrl& sourceUrl, bool trustedSource, QWidget* parent, QMap&& extra_info) + : m_sourceUrl(sourceUrl), m_trustedSource(trustedSource), m_extra_info(extra_info), m_parent(parent) {} bool InstanceImportTask::abort() @@ -287,11 +287,12 @@ void InstanceImportTask::processFlame() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = - makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = makeShared(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, pack_id, + pack_version_id, original_instance_id); } else { // FIXME: Find a way to get IDs in directly imported ZIPs - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, QString(), QString()); + inst_creation_task = + makeShared(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, QString(), QString()); } inst_creation_task->setName(*this); @@ -349,6 +350,9 @@ void InstanceImportTask::processMultiMC() // reset time played on import... because packs. instance.resetTimePlayed(); + // UUID is carried over on export, but this is a distinct instance, so give it its own + instance.regenerateUuid(); + // set a new nice name instance.setName(name()); @@ -381,8 +385,8 @@ void InstanceImportTask::processModrinth() if (original_instance_id_it != m_extra_info.constEnd()) original_instance_id = original_instance_id_it.value(); - inst_creation_task = - makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id, pack_version_id, original_instance_id); + inst_creation_task = makeShared(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, pack_id, + pack_version_id, original_instance_id); } else { QString pack_id; if (!m_sourceUrl.isEmpty()) { @@ -391,7 +395,7 @@ void InstanceImportTask::processModrinth() } // FIXME: Find a way to get the ID in directly imported ZIPs - inst_creation_task = makeShared(m_stagingPath, m_globalSettings, m_parent, pack_id); + inst_creation_task = makeShared(m_stagingPath, m_trustedSource, m_globalSettings, m_parent, pack_id); } inst_creation_task->setName(*this); diff --git a/launcher/InstanceImportTask.h b/launcher/InstanceImportTask.h index c92e229a0..29d2eba4b 100644 --- a/launcher/InstanceImportTask.h +++ b/launcher/InstanceImportTask.h @@ -43,7 +43,10 @@ class InstanceImportTask : public InstanceTask { Q_OBJECT public: - explicit InstanceImportTask(const QUrl& sourceUrl, QWidget* parent = nullptr, QMap&& extra_info = {}); + explicit InstanceImportTask(const QUrl& sourceUrl, + bool trustedSource, + QWidget* parent = nullptr, + QMap&& extra_info = {}); virtual ~InstanceImportTask() = default; bool abort() override; @@ -63,6 +66,7 @@ class InstanceImportTask : public InstanceTask { private: /* data */ QUrl m_sourceUrl; + bool m_trustedSource; QString m_archivePath; Task::Ptr m_task; enum class ModpackType { diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 1339499c7..41747fdc0 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -612,7 +612,7 @@ BaseInstance* InstanceList::getInstanceById(QString instId) const if (instId.isEmpty()) return nullptr; for (auto& inst : m_instances) { - if (inst->id() == instId) { + if (inst->id() == instId || inst->uuid() == instId) { return inst.get(); } } diff --git a/launcher/meta/BaseEntity.cpp b/launcher/meta/BaseEntity.cpp index 1869e14d3..f2f3e13fb 100644 --- a/launcher/meta/BaseEntity.cpp +++ b/launcher/meta/BaseEntity.cpp @@ -143,7 +143,7 @@ void BaseEntityLoadTask::executeTask() } } catch (const Exception& e) { - qDebug() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); + qCritical() << QString("Unable to parse file %1: %2").arg(fname, e.cause()); // just make sure it's gone and we never consider it again. FS::deletePath(fname); m_entity->m_load_status = BaseEntity::LoadStatus::NotLoaded; @@ -177,10 +177,10 @@ void BaseEntityLoadTask::executeTask() m_task->addNetAction(dl); m_task->setAskRetry(false); connect(m_task.get(), &Task::failed, this, &BaseEntityLoadTask::emitFailed); - connect(m_task.get(), &Task::succeeded, this, &BaseEntityLoadTask::emitSucceeded); connect(m_task.get(), &Task::succeeded, this, [this]() { m_entity->m_load_status = BaseEntity::LoadStatus::Remote; m_entity->m_file_sha256 = m_entity->m_sha256; + emitSucceeded(); }); connect(m_task.get(), &Task::progress, this, &Task::setProgress); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index dcc11b4be..adb94ac47 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -255,7 +255,9 @@ void MinecraftInstance::loadSpecificSettings() auto envSetting = m_settings->registerSetting("OverrideEnv", false); m_settings->registerOverride(global_settings->getSetting("Env"), envSetting); - m_settings->set("InstanceType", "OneSix"); + if (m_settings->get("InstanceType").toString() != "OneSix") { + m_settings->set("InstanceType", "OneSix"); + } } // Join server on launch, this does not have a global override diff --git a/launcher/minecraft/ShortcutUtils.cpp b/launcher/minecraft/ShortcutUtils.cpp index f74fd4972..73a170659 100644 --- a/launcher/minecraft/ShortcutUtils.cpp +++ b/launcher/minecraft/ShortcutUtils.cpp @@ -149,7 +149,7 @@ bool createInstanceShortcut(const Shortcut& shortcut, const QString& filePath) QMessageBox::critical(shortcut.parent, QObject::tr("Create Shortcut"), QObject::tr("Not supported on your platform!")); return false; #endif - args.append({ "--launch", shortcut.instance->id() }); + args.append({ "--launch", shortcut.instance->uuid() }); args.append(shortcut.extraArgs); QString shortcutPath = FS::createShortcut(filePath, appPath, args, shortcut.name, iconPath); diff --git a/launcher/minecraft/mod/Resource.cpp b/launcher/minecraft/mod/Resource.cpp index 692622521..6f9e8c972 100644 --- a/launcher/minecraft/mod/Resource.cpp +++ b/launcher/minecraft/mod/Resource.cpp @@ -46,6 +46,7 @@ void Resource::parseFile() m_internal_id = file_name; std::tie(m_size_str, m_size_info) = calculateFileSize(m_file_info); + m_hardLinkCount = FS::hardLinkCount(m_file_info.absoluteFilePath()); if (m_file_info.isDir()) { m_type = ResourceType::FOLDER; m_name = file_name; @@ -287,7 +288,7 @@ bool Resource::isSymLinkUnder(const QString& instPath) const bool Resource::isMoreThanOneHardLink() const { - return FS::hardLinkCount(m_file_info.absoluteFilePath()) > 1; + return m_hardLinkCount > 1; } auto Resource::getOriginalFileName() const -> QString diff --git a/launcher/minecraft/mod/Resource.h b/launcher/minecraft/mod/Resource.h index 485405b24..2f72cc8d8 100644 --- a/launcher/minecraft/mod/Resource.h +++ b/launcher/minecraft/mod/Resource.h @@ -35,6 +35,8 @@ #pragma once +#include + #include #include #include @@ -210,4 +212,5 @@ class Resource : public QObject { int m_resolution_ticket = 0; QString m_size_str; qint64 m_size_info; + std::uintmax_t m_hardLinkCount = 0; }; diff --git a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp index 0859c9880..0b8fe2171 100644 --- a/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp +++ b/launcher/minecraft/mod/tasks/GetModDependenciesTask.cpp @@ -37,7 +37,7 @@ static Version mcVersion(BaseInstance* inst) static ModPlatform::ModLoaderTypes mcLoaders(BaseInstance* inst) { - return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value(); + return static_cast(inst)->getPackProfile()->getSupportedModLoaders().value_or(ModPlatform::ModLoaderTypes(0)); } static bool checkDependencies(std::shared_ptr sel, diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp index 534132a6e..7f8985a09 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.cpp +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.cpp @@ -68,6 +68,7 @@ #include "minecraft/World.h" #include "minecraft/mod/tasks/LocalResourceParse.h" #include "net/ApiDownload.h" +#include "ui/dialogs/UntrustedModsDialog.h" #include "ui/pages/modplatform/OptionalModDialog.h" static const FlameAPI api; @@ -307,6 +308,31 @@ QString FlameCreationTask::getVersionForLoader(QString uid, QString loaderType, return loaderVersion; } +bool FlameCreationTask::promptForUntrustedMods() +{ + if (m_trustedSource) { + return true; + } + + QStringList untrustedMods; + + const QDir mcDir{ FS::PathCombine(m_stagingPath, "minecraft") }; + const QString modsPath{ FS::PathCombine(m_stagingPath, "minecraft/mods") }; + if (QDir(modsPath).exists()) { + QDirIterator iter{ modsPath, QDir::Files, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks }; + while (iter.hasNext()) { + untrustedMods.append(mcDir.relativeFilePath(iter.next())); + } + } + + if (untrustedMods.empty()) { + return true; + } + + UntrustedModsDialog dialog{ untrustedMods, m_parent }; + return dialog.exec() == QDialog::Accepted; +} + std::unique_ptr FlameCreationTask::createInstance() { QEventLoop loop; @@ -345,6 +371,12 @@ std::unique_ptr FlameCreationTask::createInstance() } } + if (!promptForUntrustedMods()) { + m_abort = true; + emitAborted(); + return nullptr; + } + QString loaderType; QString loaderUid; QString loaderVersion; diff --git a/launcher/modplatform/flame/FlameInstanceCreationTask.h b/launcher/modplatform/flame/FlameInstanceCreationTask.h index 221ceaf22..8710bd367 100644 --- a/launcher/modplatform/flame/FlameInstanceCreationTask.h +++ b/launcher/modplatform/flame/FlameInstanceCreationTask.h @@ -52,12 +52,13 @@ class FlameCreationTask final : public InstanceCreationTask { public: FlameCreationTask(const QString& staging_path, + bool trustedSource, SettingsObject* global_settings, QWidget* parent, QString id, QString version_id, QString original_instance_id = {}) - : InstanceCreationTask(), m_parent(parent), m_managedId(std::move(id)), m_managedVersionId(std::move(version_id)) + : InstanceCreationTask(), m_parent(parent), m_trustedSource(trustedSource), m_managedId(std::move(id)), m_managedVersionId(std::move(version_id)) { setStagingPath(staging_path); setParentSettings(global_settings); @@ -77,8 +78,11 @@ class FlameCreationTask final : public InstanceCreationTask { void validateOtherResources(QEventLoop& loop); QString getVersionForLoader(QString uid, QString loaderType, QString version, QString mcVersion); + [[nodiscard]] bool promptForUntrustedMods(); + private: QWidget* m_parent = nullptr; + bool m_trustedSource; shared_qobject_ptr m_modIdResolver; Flame::Manifest m_pack; diff --git a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp index 0cb2c547d..2ca6b082a 100644 --- a/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp +++ b/launcher/modplatform/modrinth/ModrinthInstanceCreationTask.cpp @@ -23,6 +23,7 @@ #include "settings/INISettingsObject.h" #include "ui/dialogs/CustomMessageBox.h" +#include "ui/dialogs/UntrustedModsDialog.h" #include "ui/pages/modplatform/OptionalModDialog.h" #include @@ -205,6 +206,12 @@ std::unique_ptr ModrinthCreationTask::createInstance() } } + if (!promptForUntrustedMods()) { + m_abort = true; + emitAborted(); + return nullptr; + } + QString configPath = FS::PathCombine(m_stagingPath, "instance.cfg"); auto instanceSettings = std::make_unique(configPath); auto instance = std::make_unique(m_globalSettings, std::move(instanceSettings), m_stagingPath); @@ -495,3 +502,37 @@ bool ModrinthCreationTask::parseManifest(const QString& indexPath, std::vector&, bool setInternalData = true, bool showOptionalDialog = true); + [[nodiscard]] bool promptForUntrustedMods(); + private: QWidget* m_parent = nullptr; + bool m_trustedSource; QString m_minecraft_version, m_fabric_version, m_quilt_version, m_forge_version, m_neoForge_version; QString m_managed_id, m_managed_version_id, m_managed_name; diff --git a/launcher/settings/INISettingsObject.cpp b/launcher/settings/INISettingsObject.cpp index 519b8193e..a26519a48 100644 --- a/launcher/settings/INISettingsObject.cpp +++ b/launcher/settings/INISettingsObject.cpp @@ -51,19 +51,33 @@ void INISettingsObject::setFilePath(const QString& filePath) bool INISettingsObject::reload() { - return m_ini.loadFile(m_filePath) && SettingsObject::reload(); + if (!m_ini.loadFile(m_filePath)) { + return false; + } + + bool suspendSavePrev = m_suspendSave; + bool doSavePrev = m_doSave; + + m_suspendSave = true; + bool result = SettingsObject::reload(); + + m_suspendSave = suspendSavePrev; + m_doSave = doSavePrev; + return result; } void INISettingsObject::suspendSave() { + Q_ASSERT(!m_suspendSave); m_suspendSave = true; } void INISettingsObject::resumeSave() { + Q_ASSERT(m_suspendSave); m_suspendSave = false; if (m_doSave) { - m_ini.saveFile(m_filePath); + doSave(); } } @@ -91,6 +105,7 @@ void INISettingsObject::doSave() if (m_suspendSave) { m_doSave = true; } else { + qDebug() << "Saving INI settings to" << m_filePath; m_ini.saveFile(m_filePath); } } diff --git a/launcher/settings/Setting.cpp b/launcher/settings/Setting.cpp index 1e861e36b..209e16579 100644 --- a/launcher/settings/Setting.cpp +++ b/launcher/settings/Setting.cpp @@ -38,7 +38,9 @@ QVariant Setting::defValue() const void Setting::set(QVariant value) { - emit SettingChanged(*this, value); + if (const auto currentValue = get(); value != currentValue) { + emit SettingChanged(*this, value); + } } void Setting::reset() diff --git a/launcher/ui/dialogs/InstallLoaderDialog.cpp b/launcher/ui/dialogs/InstallLoaderDialog.cpp index 8fd63a416..b5c715e40 100644 --- a/launcher/ui/dialogs/InstallLoaderDialog.cpp +++ b/launcher/ui/dialogs/InstallLoaderDialog.cpp @@ -163,6 +163,9 @@ void InstallLoaderDialog::done(int result) auto* page = pageCast(container->selectedPage()); if (page->selectedVersion()) { profile->setComponentVersion(page->id(), page->selectedVersion()->descriptor()); + if (auto component = profile->getComponent(page->id())) { + component->setEnabled(true); + } profile->resolve(Net::Mode::Online); } } diff --git a/launcher/ui/dialogs/ResourceDownloadDialog.cpp b/launcher/ui/dialogs/ResourceDownloadDialog.cpp index bcb30c761..c222217d4 100644 --- a/launcher/ui/dialogs/ResourceDownloadDialog.cpp +++ b/launcher/ui/dialogs/ResourceDownloadDialog.cpp @@ -305,7 +305,7 @@ QList ModDownloadDialog::getPages() { QList pages; - auto loaders = static_cast(m_instance)->getPackProfile()->getSupportedModLoaders().value(); + auto loaders = static_cast(m_instance)->getPackProfile()->getSupportedModLoaders().value_or(ModPlatform::ModLoaderTypes(0)); if (ModrinthAPI::validateModLoaders(loaders)) { auto* page = ModrinthModPage::create(this, *m_instance); diff --git a/launcher/ui/dialogs/UntrustedModsDialog.cpp b/launcher/ui/dialogs/UntrustedModsDialog.cpp new file mode 100644 index 000000000..919af7f65 --- /dev/null +++ b/launcher/ui/dialogs/UntrustedModsDialog.cpp @@ -0,0 +1,26 @@ +#include "UntrustedModsDialog.h" +#include "ui_UntrustedModsDialog.h" + +#include +#include +#include +#include + +UntrustedModsDialog::UntrustedModsDialog(const QStringList& paths, QWidget* parent) : QDialog{ parent }, m_ui{ new Ui::UntrustedModsDialog } +{ + m_ui->setupUi(this); + m_ui->modList->addItems(paths); + + auto* ok = m_ui->buttonBox->button(QDialogButtonBox::Ok); + ok->setEnabled(false); + + connect(m_ui->confirmCheckbox, &QAbstractButton::clicked, ok, &QWidget::setEnabled); + + m_ui->confirmCheckbox->setEnabled(false); + QTimer::singleShot(3000, this, [this] { m_ui->confirmCheckbox->setEnabled(true); }); +} + +UntrustedModsDialog::~UntrustedModsDialog() +{ + delete m_ui; +} diff --git a/launcher/ui/dialogs/UntrustedModsDialog.h b/launcher/ui/dialogs/UntrustedModsDialog.h new file mode 100644 index 000000000..23b1249ef --- /dev/null +++ b/launcher/ui/dialogs/UntrustedModsDialog.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include + +namespace Ui { +class UntrustedModsDialog; +} + +class UntrustedModsDialog : public QDialog { + Q_OBJECT + public: + explicit UntrustedModsDialog(const QStringList& paths, QWidget* parent = nullptr); + ~UntrustedModsDialog() override; + + private: + Ui::UntrustedModsDialog* m_ui; +}; diff --git a/launcher/ui/dialogs/UntrustedModsDialog.ui b/launcher/ui/dialogs/UntrustedModsDialog.ui new file mode 100644 index 000000000..d673aa456 --- /dev/null +++ b/launcher/ui/dialogs/UntrustedModsDialog.ui @@ -0,0 +1,88 @@ + + + UntrustedModsDialog + + + + 0 + 0 + 635 + 269 + + + + Easy There! + + + + + + <html><head/><body><p>The modpack you are installing includes mods which are not hosted on Modrinth or CurseForge:</p></body></html> + + + + + + + + + + <html><head/><body><p><b>Malicious mods are often distributed through links sent on platforms such as Discord.</b></p><p>We strongly recommend only importing modpacks from trusted sources.</p></body></html> + + + + + + + I trust this modpack and wish to proceed regardless + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + UntrustedModsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + UntrustedModsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/launcher/ui/pages/instance/InstanceSettingsPage.h b/launcher/ui/pages/instance/InstanceSettingsPage.h index 79d5944eb..cbb913889 100644 --- a/launcher/ui/pages/instance/InstanceSettingsPage.h +++ b/launcher/ui/pages/instance/InstanceSettingsPage.h @@ -46,7 +46,7 @@ class InstanceSettingsPage : public MinecraftSettingsWidget, public BasePage { public: explicit InstanceSettingsPage(MinecraftInstance* instance, QWidget* parent = nullptr) : MinecraftSettingsWidget(instance, parent) { - connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::saveSettings); + connect(APPLICATION, &Application::globalSettingsAboutToOpen, this, &InstanceSettingsPage::apply); connect(APPLICATION, &Application::globalSettingsApplied, this, &InstanceSettingsPage::loadSettings); } ~InstanceSettingsPage() override {} @@ -55,6 +55,7 @@ class InstanceSettingsPage : public MinecraftSettingsWidget, public BasePage { QString id() const override { return "settings"; } bool apply() override { + SettingsObject::Lock lock(m_instance->settings()); saveSettings(); return true; } diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index d2683fa92..0dbf6df2f 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -359,7 +359,7 @@ void ModrinthManagedPackPage::update() { auto customURL = m_inst->settings()->get("ManagedPackURL").toString().trimmed(); if (m_inst->getManagedPackID().isEmpty() && !customURL.isEmpty()) { - updatePack(customURL); + updatePack(customURL, false); return; } auto index = ui->versionsComboBox->currentIndex(); @@ -369,7 +369,7 @@ void ModrinthManagedPackPage::update() } auto version = m_pack.versions.at(index); - updatePack(version.downloadUrl, version.fileId.toString(), version.version); + updatePack(version.downloadUrl, true, version.fileId.toString(), version.version); } void ModrinthManagedPackPage::updateFromFile() @@ -378,7 +378,7 @@ void ModrinthManagedPackPage::updateFromFile() if (output.isEmpty()) return; - updatePack(output); + updatePack(output, false); } // FLAME @@ -488,7 +488,7 @@ void FlameManagedPackPage::update() { auto customURL = m_inst->settings()->get("ManagedPackURL").toString().trimmed(); if (m_inst->getManagedPackID().isEmpty() && !customURL.isEmpty()) { - updatePack(customURL); + updatePack(customURL, false); return; } auto index = ui->versionsComboBox->currentIndex(); @@ -498,7 +498,7 @@ void FlameManagedPackPage::update() } auto version = m_pack.versions.at(index); - updatePack(version.downloadUrl, version.fileId.toString()); + updatePack(version.downloadUrl, true, version.fileId.toString()); } void FlameManagedPackPage::updateFromFile() @@ -507,10 +507,10 @@ void FlameManagedPackPage::updateFromFile() if (output.isEmpty()) return; - updatePack(output); + updatePack(output, false); } -void ManagedPackPage::updatePack(const QUrl& url, QString versionID, QString versionName) +void ManagedPackPage::updatePack(const QUrl& url, bool trusted, QString versionID, QString versionName) { QMap extra_info; // NOTE: Don't use 'm_pack.id' here, since we didn't completely parse all the metadata for the pack, including this field. @@ -518,7 +518,7 @@ void ManagedPackPage::updatePack(const QUrl& url, QString versionID, QString ver extra_info.insert("pack_version_id", versionID); extra_info.insert("original_instance_id", m_inst->id()); - auto extracted = new InstanceImportTask(url, this, std::move(extra_info)); + auto extracted = new InstanceImportTask(url, trusted, this, std::move(extra_info)); if (versionName.isEmpty()) { extracted->setName(m_inst->name()); diff --git a/launcher/ui/pages/instance/ManagedPackPage.h b/launcher/ui/pages/instance/ManagedPackPage.h index 4b7332896..40d1deee7 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.h +++ b/launcher/ui/pages/instance/ManagedPackPage.h @@ -86,7 +86,7 @@ class ManagedPackPage : public QWidget, public BasePage { */ bool runUpdateTask(InstanceTask*); - void updatePack(const QUrl& url, QString versionID = {}, QString versionName = {}); + void updatePack(const QUrl& url, bool trusted, QString versionID = {}, QString versionName = {}); protected: InstanceWindow* m_instance_window = nullptr; diff --git a/launcher/ui/pages/instance/ModFolderPage.cpp b/launcher/ui/pages/instance/ModFolderPage.cpp index 99c78647c..8bd082db5 100644 --- a/launcher/ui/pages/instance/ModFolderPage.cpp +++ b/launcher/ui/pages/instance/ModFolderPage.cpp @@ -167,10 +167,8 @@ void ModFolderPage::downloadMods() } auto* profile = static_cast(m_instance)->getPackProfile(); - if (!profile->getModLoaders().has_value()) { - if (handleNoModLoader()) { - return; - } + if (!profile->getModLoaders().has_value() && handleNoModLoader()) { + return; } m_downloadDialog = new ResourceDownload::ModDownloadDialog(this, m_model, m_instance); @@ -227,10 +225,8 @@ void ModFolderPage::updateMods(bool includeDeps) } auto* profile = static_cast(m_instance)->getPackProfile(); - if (!profile->getModLoaders().has_value()) { - if (handleNoModLoader()) { - return; - } + if (!profile->getModLoaders().has_value() && handleNoModLoader()) { + return; } if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!")); @@ -337,10 +333,8 @@ void ModFolderPage::changeModVersion() } auto* profile = static_cast(m_instance)->getPackProfile(); - if (!profile->getModLoaders().has_value()) { - if (handleNoModLoader()) { - return; - } + if (!profile->getModLoaders().has_value() && handleNoModLoader()) { + return; } if (APPLICATION->settings()->get("ModMetadataDisabled").toBool()) { QMessageBox::critical(this, tr("Error"), tr("Mod updates are unavailable when metadata is disabled!")); @@ -434,12 +428,21 @@ inline bool ModFolderPage::handleNoModLoader() // Should be safe auto* profile = static_cast(this->m_instance)->getPackProfile(); InstallLoaderDialog dialog(profile, QString(), this); - bool ret = dialog.exec() != 0; + // true if the user went through the install loader dialog + // false if the dialog got canceled/closed + bool dialogAccepted = dialog.exec() != 0; this->m_container->refreshContainer(); - // returning negation of dialog.exec which'll be true if the install loader dialog got canceled/closed - // and false if the user went through and installed a loader - return !ret; + if (!dialogAccepted) { + return true; + } + if (!profile->getModLoaders().has_value()) { + CustomMessageBox::selectable( + this, tr("Error"), tr("No mod loader was installed. Please try again."), QMessageBox::Warning) + ->show(); + return true; + } + return false; } // Nothing happens the dialog is already closing // returning true so the caller doesn't go and continue with opening it's dialog without a mod loader diff --git a/launcher/ui/pages/modplatform/ImportPage.cpp b/launcher/ui/pages/modplatform/ImportPage.cpp index 6e783014f..c93fcfa4a 100644 --- a/launcher/ui/pages/modplatform/ImportPage.cpp +++ b/launcher/ui/pages/modplatform/ImportPage.cpp @@ -118,7 +118,7 @@ void ImportPage::updateState() if (fi.exists() && (isZip || isMRPack)) { auto extra_info = QMap(m_extra_info); qDebug() << "Pack Extra Info" << extra_info << m_extra_info; - dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); + dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, false, this, std::move(extra_info))); dialog->setSuggestedIcon("default"); } } else if (url.scheme() == "curseforge") { @@ -163,7 +163,7 @@ void ImportPage::updateState() extra_info.insert("pack_id", addonId); extra_info.insert("pack_version_id", fileId); - dialog->setSuggestedPack(pack_name, new InstanceImportTask(dl_url, this, std::move(extra_info))); + dialog->setSuggestedPack(pack_name, new InstanceImportTask(dl_url, false, this, std::move(extra_info))); dialog->setSuggestedIcon("default"); } else { @@ -183,7 +183,7 @@ void ImportPage::updateState() // hook, line and sinker. QFileInfo fi(url.fileName()); auto extra_info = QMap(m_extra_info); - dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, this, std::move(extra_info))); + dialog->setSuggestedPack(fi.completeBaseName(), new InstanceImportTask(url, false, this, std::move(extra_info))); dialog->setSuggestedIcon("default"); } } else { diff --git a/launcher/ui/pages/modplatform/flame/FlamePage.cpp b/launcher/ui/pages/modplatform/flame/FlamePage.cpp index 336133819..9ef1a768b 100644 --- a/launcher/ui/pages/modplatform/flame/FlamePage.cpp +++ b/launcher/ui/pages/modplatform/flame/FlamePage.cpp @@ -241,7 +241,7 @@ void FlamePage::suggestCurrent() extra_info.insert("pack_id", m_current->addonId.toString()); extra_info.insert("pack_version_id", version.fileId.toString()); - m_dialog->setSuggestedPack(m_current->name, new InstanceImportTask(version.downloadUrl, this, std::move(extra_info))); + m_dialog->setSuggestedPack(m_current->name, new InstanceImportTask(version.downloadUrl, true, this, std::move(extra_info))); QString editedLogoName = "curseforge_" + m_current->logoName; m_listModel->getLogo(m_current->logoName, m_current->logoUrl, [this, editedLogoName](QString logo) { m_dialog->setSuggestedIconFromFile(logo, editedLogoName); }); diff --git a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp index 4798583bd..b9b6f0c0a 100644 --- a/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp +++ b/launcher/ui/pages/modplatform/modrinth/ModrinthPage.cpp @@ -318,7 +318,8 @@ void ModrinthPage::suggestCurrent() extra_info.insert("pack_id", m_current->addonId.toString()); extra_info.insert("pack_version_id", ver.fileId.toString()); - m_dialog->setSuggestedPack(m_current->name, ver.version, new InstanceImportTask(ver.downloadUrl, this, std::move(extra_info))); + m_dialog->setSuggestedPack(m_current->name, ver.version, + new InstanceImportTask(ver.downloadUrl, true, this, std::move(extra_info))); QString editedLogoName = "modrinth_" + m_current->logoName; m_model->getLogo(m_current->logoName, m_current->logoUrl, [this, editedLogoName](QString logo) { m_dialog->setSuggestedIconFromFile(logo, editedLogoName); }); diff --git a/launcher/ui/themes/ThemeManager.mm b/launcher/ui/themes/ThemeManager.mm index d9fc291b6..78e8244a4 100644 --- a/launcher/ui/themes/ThemeManager.mm +++ b/launcher/ui/themes/ThemeManager.mm @@ -31,15 +31,6 @@ window.titlebarAppearsTransparent = YES; window.backgroundColor = [NSColor colorWithRed:color.redF() green:color.greenF() blue:color.blueF() alpha:color.alphaF()]; - // Unfortunately there seems to be no easy way to set the titlebar text color. - // The closest we can do without dubious hacks is set the dark/light mode state based on the brightness of the - // background color, which should at least make the text readable even if we can't use the theme's text color. - // It's a good idea to set this anyway since it also affects some other UI elements like text shadows (PrismLauncher#3825). - if (color.lightnessF() < 0.5) { - window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameDarkAqua]; - } else { - window.appearance = [NSAppearance appearanceNamed:NSAppearanceNameAqua]; - } } void ThemeManager::setTitlebarColorOfAllWindowsOnMac(QColor color) diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index e13c847d0..869c5cb62 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -167,8 +167,6 @@ void JavaSettingsWidget::saveSettings() else settings = APPLICATION->settings(); - SettingsObject::Lock lock(settings); - // Java Install Settings bool javaInstall = m_instance == nullptr || m_ui->javaInstallationGroupBox->isChecked(); diff --git a/launcher/ui/widgets/JavaSettingsWidget.ui b/launcher/ui/widgets/JavaSettingsWidget.ui index 803bad0d6..99ccd0414 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.ui +++ b/launcher/ui/widgets/JavaSettingsWidget.ui @@ -7,13 +7,13 @@ 0 0 500 - 1000 + 1000 Form - + @@ -29,92 +29,92 @@ false - + Auto-&detect Java version - - + + - - - &Detect - - + + + &Detect + + - &Browse + &Browse - - - - Qt::Orientation::Horizontal - - - - 40 - 20 - - - + + + + Qt::Orientation::Horizontal + + + + 40 + 20 + + + - + - + - Test S&ettings + Test S&ettings - + - Open Java &Downloader + Open Java &Downloader - - - Qt::Orientation::Horizontal + + + Qt::Orientation::Horizontal + + + + 0 + 0 + - - - 0 - 0 - - - + - - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::Fixed - - - - 0 - 6 - - - - - + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed + + + + 0 + 6 + + + + + Automatically downloads and selects the Java build recommended by Mojang. @@ -125,31 +125,29 @@ - - - If enabled, the launcher will not check if an instance is compatible with the selected Java - version. - - - - Skip Java compatibility checks - - + + + If enabled, the launcher will not check if an instance is compatible with the selected Java version. + + + Skip Java compatibility checks + + + + + - - - - - - - Java &Executable - - - javaPathTextBox - - - - + + + + Java &Executable + + + javaPathTextBox + + + + If enabled, the launcher won't prompt you to choose a Java version if one is not found on startup. @@ -160,20 +158,20 @@ - - - Qt::Orientation::Vertical - - - QSizePolicy::Policy::Fixed + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Fixed - - - 0 - 6 - + + + 0 + 6 + - + @@ -192,172 +190,172 @@ false - - - - - - - M&inimum Memory Usage: - - - minMemSpinBox - - - - - - - - - - 0 - 0 - - - - The amount of memory Minecraft is started with. - - - MiB - - - 8 - - - 1048576 - - - 128 - - - 256 - - - - - - - (-Xms) - - - - - - - - - Ma&ximum Memory Usage: - - - maxMemSpinBox - - - - - - - - - - 0 - 0 - - - - The maximum amount of memory Minecraft is allowed to use. - - - MiB - - - 8 - - - 1048576 - - - 128 - - - 1024 - - - - - - - (-Xmx) - - - - - - - - - &PermGen Size: - - - permGenSpinBox - - - - - - - - - - 0 - 0 - - - - The amount of memory available to store loaded Java classes. - - - MiB - - - 4 - - - 1048576 - - - 8 - - - 64 - - - - - - - (-XX:PermSize) - - - - - - - - - - - Warn when there is not enough memory available - - - - - - - Memory Notice - - - + + + + + + + M&inimum Memory Usage: + + + minMemSpinBox + + + + + + + + + + 0 + 0 + + + + The amount of memory Minecraft is started with. + + + MiB + + + 8 + + + 1048576 + + + 128 + + + 256 + + + + + + + (-Xms) + + + + + + + + + Ma&ximum Memory Usage: + + + maxMemSpinBox + + + + + + + + + + 0 + 0 + + + + The maximum amount of memory Minecraft is allowed to use. + + + MiB + + + 8 + + + 1048576 + + + 128 + + + 1024 + + + + + + + (-Xmx) + + + + + + + + + &PermGen Size: + + + permGenSpinBox + + + + + + + + + + 0 + 0 + + + + The amount of memory available to store loaded Java classes. + + + MiB + + + 4 + + + 1048576 + + + 8 + + + 64 + + + + + + + (-XX:PermSize) + + + + + + + + + + + Warn when there is not enough free memory + + + + + + + Memory Notice + + + diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.cpp b/launcher/ui/widgets/MinecraftSettingsWidget.cpp index 1b8576dcc..a2930aea1 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.cpp +++ b/launcher/ui/widgets/MinecraftSettingsWidget.cpp @@ -370,215 +370,211 @@ void MinecraftSettingsWidget::saveSettings() else settings = APPLICATION->settings(); - { - SettingsObject::Lock lock(settings); - - // Console - bool console = m_instance == nullptr || m_ui->consoleSettingsBox->isChecked(); + // Console + bool console = m_instance == nullptr || m_ui->consoleSettingsBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverrideConsole", console); + if (m_instance != nullptr) + settings->set("OverrideConsole", console); - if (console) { - settings->set("ShowConsole", m_ui->showConsoleCheck->isChecked()); - settings->set("AutoCloseConsole", m_ui->autoCloseConsoleCheck->isChecked()); - settings->set("ShowConsoleOnError", m_ui->showConsoleErrorCheck->isChecked()); - } else { - settings->reset("ShowConsole"); - settings->reset("AutoCloseConsole"); - settings->reset("ShowConsoleOnError"); - } + if (console) { + settings->set("ShowConsole", m_ui->showConsoleCheck->isChecked()); + settings->set("AutoCloseConsole", m_ui->autoCloseConsoleCheck->isChecked()); + settings->set("ShowConsoleOnError", m_ui->showConsoleErrorCheck->isChecked()); + } else { + settings->reset("ShowConsole"); + settings->reset("AutoCloseConsole"); + settings->reset("ShowConsoleOnError"); + } - // Game Window - bool window = m_instance == nullptr || m_ui->windowSizeGroupBox->isChecked(); + // Game Window + bool window = m_instance == nullptr || m_ui->windowSizeGroupBox->isChecked(); - if (m_instance != nullptr) { - settings->set("OverrideWindow", window); - settings->set("OverrideMiscellaneous", window); - } + if (m_instance != nullptr) { + settings->set("OverrideWindow", window); + settings->set("OverrideMiscellaneous", window); + } - if (window) { - settings->set("LaunchMaximized", m_ui->maximizedCheckBox->isChecked()); - settings->set("MinecraftWinWidth", m_ui->windowWidthSpinBox->value()); - settings->set("MinecraftWinHeight", m_ui->windowHeightSpinBox->value()); - settings->set("CloseAfterLaunch", m_ui->closeAfterLaunchCheck->isChecked()); - settings->set("QuitAfterGameStop", m_ui->quitAfterGameStopCheck->isChecked()); - settings->set("CopyIngameScreenshots", m_ui->copyIngameScreenshotsCheck->isChecked()); - } else { - settings->reset("LaunchMaximized"); - settings->reset("MinecraftWinWidth"); - settings->reset("MinecraftWinHeight"); - settings->reset("CloseAfterLaunch"); - settings->reset("QuitAfterGameStop"); - settings->reset("CopyIngameScreenshots"); - } + if (window) { + settings->set("LaunchMaximized", m_ui->maximizedCheckBox->isChecked()); + settings->set("MinecraftWinWidth", m_ui->windowWidthSpinBox->value()); + settings->set("MinecraftWinHeight", m_ui->windowHeightSpinBox->value()); + settings->set("CloseAfterLaunch", m_ui->closeAfterLaunchCheck->isChecked()); + settings->set("QuitAfterGameStop", m_ui->quitAfterGameStopCheck->isChecked()); + settings->set("CopyIngameScreenshots", m_ui->copyIngameScreenshotsCheck->isChecked()); + } else { + settings->reset("LaunchMaximized"); + settings->reset("MinecraftWinWidth"); + settings->reset("MinecraftWinHeight"); + settings->reset("CloseAfterLaunch"); + settings->reset("QuitAfterGameStop"); + settings->reset("CopyIngameScreenshots"); + } - // Custom Commands - bool custcmd = m_instance == nullptr || m_ui->customCommands->checked(); + // Custom Commands + bool custcmd = m_instance == nullptr || m_ui->customCommands->checked(); - if (m_instance != nullptr) - settings->set("OverrideCommands", custcmd); + if (m_instance != nullptr) + settings->set("OverrideCommands", custcmd); - if (custcmd) { - settings->set("PreLaunchCommand", m_ui->customCommands->prelaunchCommand()); - settings->set("WrapperCommand", m_ui->customCommands->wrapperCommand()); - settings->set("PostExitCommand", m_ui->customCommands->postexitCommand()); - } else { - settings->reset("PreLaunchCommand"); - settings->reset("WrapperCommand"); - settings->reset("PostExitCommand"); - } + if (custcmd) { + settings->set("PreLaunchCommand", m_ui->customCommands->prelaunchCommand()); + settings->set("WrapperCommand", m_ui->customCommands->wrapperCommand()); + settings->set("PostExitCommand", m_ui->customCommands->postexitCommand()); + } else { + settings->reset("PreLaunchCommand"); + settings->reset("WrapperCommand"); + settings->reset("PostExitCommand"); + } - // Environment Variables - auto env = m_instance == nullptr || m_ui->environmentVariables->override(); + // Environment Variables + auto env = m_instance == nullptr || m_ui->environmentVariables->override(); - if (m_instance != nullptr) - settings->set("OverrideEnv", env); + if (m_instance != nullptr) + settings->set("OverrideEnv", env); - if (env) - settings->set("Env", Json::fromMap(m_ui->environmentVariables->value())); - else - settings->reset("Env"); + if (env) + settings->set("Env", Json::fromMap(m_ui->environmentVariables->value())); + else + settings->reset("Env"); - // Workarounds - bool workarounds = m_instance == nullptr || m_ui->nativeWorkaroundsGroupBox->isChecked(); + // Workarounds + bool workarounds = m_instance == nullptr || m_ui->nativeWorkaroundsGroupBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverrideNativeWorkarounds", workarounds); + if (m_instance != nullptr) + settings->set("OverrideNativeWorkarounds", workarounds); - if (workarounds) { - settings->set("UseNativeGLFW", m_ui->useNativeGLFWCheck->isChecked()); - settings->set("CustomGLFWPath", m_ui->lineEditGLFWPath->text()); - settings->set("UseNativeOpenAL", m_ui->useNativeOpenALCheck->isChecked()); - settings->set("CustomOpenALPath", m_ui->lineEditOpenALPath->text()); - } else { - settings->reset("UseNativeGLFW"); - settings->reset("CustomGLFWPath"); - settings->reset("UseNativeOpenAL"); - settings->reset("CustomOpenALPath"); - } + if (workarounds) { + settings->set("UseNativeGLFW", m_ui->useNativeGLFWCheck->isChecked()); + settings->set("CustomGLFWPath", m_ui->lineEditGLFWPath->text()); + settings->set("UseNativeOpenAL", m_ui->useNativeOpenALCheck->isChecked()); + settings->set("CustomOpenALPath", m_ui->lineEditOpenALPath->text()); + } else { + settings->reset("UseNativeGLFW"); + settings->reset("CustomGLFWPath"); + settings->reset("UseNativeOpenAL"); + settings->reset("CustomOpenALPath"); + } - // Performance - bool performance = m_instance == nullptr || m_ui->perfomanceGroupBox->isChecked(); + // Performance + bool performance = m_instance == nullptr || m_ui->perfomanceGroupBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverridePerformance", performance); + if (m_instance != nullptr) + settings->set("OverridePerformance", performance); - if (performance) { - settings->set("EnableFeralGamemode", m_ui->enableFeralGamemodeCheck->isChecked()); - settings->set("EnableMangoHud", m_ui->enableMangoHud->isChecked()); - settings->set("UseDiscreteGpu", m_ui->useDiscreteGpuCheck->isChecked()); - settings->set("UseZink", m_ui->useZink->isChecked()); - } else { - settings->reset("EnableFeralGamemode"); - settings->reset("EnableMangoHud"); - settings->reset("UseDiscreteGpu"); - settings->reset("UseZink"); - } - // Elyby - bool elyby = m_instance == nullptr || m_ui->elybyGroupBox->isChecked(); + if (performance) { + settings->set("EnableFeralGamemode", m_ui->enableFeralGamemodeCheck->isChecked()); + settings->set("EnableMangoHud", m_ui->enableMangoHud->isChecked()); + settings->set("UseDiscreteGpu", m_ui->useDiscreteGpuCheck->isChecked()); + settings->set("UseZink", m_ui->useZink->isChecked()); + } else { + settings->reset("EnableFeralGamemode"); + settings->reset("EnableMangoHud"); + settings->reset("UseDiscreteGpu"); + settings->reset("UseZink"); + } + // Elyby + bool elyby = m_instance == nullptr || m_ui->elybyGroupBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverrideElyby", elyby); + if (m_instance != nullptr) + settings->set("OverrideElyby", elyby); - if (elyby) { - settings->set("UseElySkins", m_ui->elySkinSystemComboBox->currentIndex()); - } else { - settings->reset("UseElySkins"); - } + if (elyby) { + settings->set("UseElySkins", m_ui->elySkinSystemComboBox->currentIndex()); + } else { + settings->reset("UseElySkins"); + } - bool injectors = m_instance == nullptr || m_ui->authGroupBox->isChecked(); + bool injectors = m_instance == nullptr || m_ui->authGroupBox->isChecked(); - if (m_instance != nullptr) { - settings->set("OverrideInjectors", injectors); - } + if (m_instance != nullptr) { + settings->set("OverrideInjectors", injectors); + } - if (injectors) { - settings->set("InjectorUid", m_ui->injectorImplCombo->currentData(Qt::UserRole).toString()); - settings->set("InjectorVersion", m_injectorVersionToSave); - } else { - settings->reset("InjectorUid"); - settings->reset("InjectorVersion"); - } + if (injectors) { + settings->set("InjectorUid", m_ui->injectorImplCombo->currentData(Qt::UserRole).toString()); + settings->set("InjectorVersion", m_injectorVersionToSave); + } else { + settings->reset("InjectorUid"); + settings->reset("InjectorVersion"); + } - bool discord = m_instance == nullptr || m_ui->discordGroupBox->isChecked(); + bool discord = m_instance == nullptr || m_ui->discordGroupBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverrideDiscord", discord); + if (m_instance != nullptr) + settings->set("OverrideDiscord", discord); - if (discord) { - settings->set("EnableDiscordRichPresence", m_ui->enableRichPresenceCheck->isChecked()); - } else { - settings->reset("EnableDiscordRichPresence"); - } + if (discord) { + settings->set("EnableDiscordRichPresence", m_ui->enableRichPresenceCheck->isChecked()); + } else { + settings->reset("EnableDiscordRichPresence"); + } - // Game time - bool gameTime = m_instance == nullptr || m_ui->gameTimeGroupBox->isChecked(); + // Game time + bool gameTime = m_instance == nullptr || m_ui->gameTimeGroupBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverrideGameTime", gameTime); + if (m_instance != nullptr) + settings->set("OverrideGameTime", gameTime); - if (gameTime) { - settings->set("ShowGameTime", m_ui->showGameTime->isChecked()); - settings->set("RecordGameTime", m_ui->recordGameTime->isChecked()); - } else { - settings->reset("ShowGameTime"); - settings->reset("RecordGameTime"); - } + if (gameTime) { + settings->set("ShowGameTime", m_ui->showGameTime->isChecked()); + settings->set("RecordGameTime", m_ui->recordGameTime->isChecked()); + } else { + settings->reset("ShowGameTime"); + settings->reset("RecordGameTime"); + } - if (m_instance == nullptr) { - settings->set("ShowGlobalGameTime", m_ui->showGlobalGameTime->isChecked()); - settings->set("ShowGameTimeWithoutDays", m_ui->showGameTimeWithoutDays->isChecked()); - } + if (m_instance == nullptr) { + settings->set("ShowGlobalGameTime", m_ui->showGlobalGameTime->isChecked()); + settings->set("ShowGameTimeWithoutDays", m_ui->showGameTimeWithoutDays->isChecked()); + } - if (m_instance != nullptr) { - // Join server on launch - bool joinServerOnLaunch = m_ui->serverJoinGroupBox->isChecked(); - settings->set("JoinServerOnLaunch", joinServerOnLaunch); - if (joinServerOnLaunch) { - if (m_ui->serverJoinAddressButton->isChecked() || !m_quickPlaySingleplayer) { - settings->set("JoinServerOnLaunchAddress", m_ui->serverJoinAddress->text()); - settings->reset("JoinWorldOnLaunch"); - } else { - settings->set("JoinWorldOnLaunch", m_ui->worldsCb->currentText()); - settings->reset("JoinServerOnLaunchAddress"); - } + if (m_instance != nullptr) { + // Join server on launch + bool joinServerOnLaunch = m_ui->serverJoinGroupBox->isChecked(); + settings->set("JoinServerOnLaunch", joinServerOnLaunch); + if (joinServerOnLaunch) { + if (m_ui->serverJoinAddressButton->isChecked() || !m_quickPlaySingleplayer) { + settings->set("JoinServerOnLaunchAddress", m_ui->serverJoinAddress->text()); + settings->reset("JoinWorldOnLaunch"); } else { + settings->set("JoinWorldOnLaunch", m_ui->worldsCb->currentText()); settings->reset("JoinServerOnLaunchAddress"); - settings->reset("JoinWorldOnLaunch"); } + } else { + settings->reset("JoinServerOnLaunchAddress"); + settings->reset("JoinWorldOnLaunch"); + } - // Use an account for this instance - bool useAccountForInstance = m_ui->instanceAccountGroupBox->isChecked(); - settings->set("UseAccountForInstance", useAccountForInstance); - if (useAccountForInstance) { - int accountIndex = m_ui->instanceAccountSelector->currentIndex(); - - if (accountIndex != -1) { - const MinecraftAccountPtr account = APPLICATION->accounts()->at(accountIndex); - if (account != nullptr) { - QString id = QString("%1:%2").arg(account->profileId(), account->typeString()); - if (account->accountType() == AccountType::Custom) { - id.append(QString(":%1").arg(account->accountData()->authUrl)); - } - settings->set("InstanceAccountId", id); + // Use an account for this instance + bool useAccountForInstance = m_ui->instanceAccountGroupBox->isChecked(); + settings->set("UseAccountForInstance", useAccountForInstance); + if (useAccountForInstance) { + int accountIndex = m_ui->instanceAccountSelector->currentIndex(); + + if (accountIndex != -1) { + const MinecraftAccountPtr account = APPLICATION->accounts()->at(accountIndex); + if (account != nullptr) { + QString id = QString("%1:%2").arg(account->profileId(), account->typeString()); + if (account->accountType() == AccountType::Custom) { + id.append(QString(":%1").arg(account->accountData()->authUrl)); } + settings->set("InstanceAccountId", id); } - } else { - settings->reset("InstanceAccountId"); } + } else { + settings->reset("InstanceAccountId"); } + } - bool overrideLegacySettings = m_instance == nullptr || m_ui->legacySettingsGroupBox->isChecked(); + bool overrideLegacySettings = m_instance == nullptr || m_ui->legacySettingsGroupBox->isChecked(); - if (m_instance != nullptr) - settings->set("OverrideLegacySettings", overrideLegacySettings); + if (m_instance != nullptr) + settings->set("OverrideLegacySettings", overrideLegacySettings); - if (overrideLegacySettings) { - settings->set("OnlineFixes", m_ui->onlineFixes->isChecked()); - } else { - settings->reset("OnlineFixes"); - } + if (overrideLegacySettings) { + settings->set("OnlineFixes", m_ui->onlineFixes->isChecked()); + } else { + settings->reset("OnlineFixes"); } if (m_javaSettings != nullptr) diff --git a/launcher/ui/widgets/MinecraftSettingsWidget.h b/launcher/ui/widgets/MinecraftSettingsWidget.h index b5d70606f..a6b94cd8a 100644 --- a/launcher/ui/widgets/MinecraftSettingsWidget.h +++ b/launcher/ui/widgets/MinecraftSettingsWidget.h @@ -64,7 +64,10 @@ class MinecraftSettingsWidget : public QWidget { void chooseInjectorVersion(); void resetInjectorVersion(); + protected: MinecraftInstance* m_instance; + + public: Ui::MinecraftSettingsWidget* m_ui; JavaSettingsWidget* m_javaSettings = nullptr; bool m_quickPlaySingleplayer = false; diff --git a/launcher/ui/widgets/ModFilterWidget.cpp b/launcher/ui/widgets/ModFilterWidget.cpp index 6fab2b2a5..7ad21ebdb 100644 --- a/launcher/ui/widgets/ModFilterWidget.cpp +++ b/launcher/ui/widgets/ModFilterWidget.cpp @@ -234,7 +234,7 @@ void ModFilterWidget::prepareBasicFilter() loaders |= ModPlatform::getModLoaderFromString(loader); } } else { - loaders = m_instance->getPackProfile()->getSupportedModLoaders().value(); + loaders = m_instance->getPackProfile()->getSupportedModLoaders().value_or(ModPlatform::ModLoaderTypes(0)); } ui->neoForge->setChecked(loaders & ModPlatform::NeoForge); ui->forge->setChecked(loaders & ModPlatform::Forge);