From 838e7fb8d2075f517a81301484e8f19ee7f95bf4 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Thu, 9 Apr 2026 23:33:42 +0300 Subject: [PATCH 01/13] chore: bump to 11.0.1 Signed-off-by: Trial97 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 80977e06e..6e5fe090b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,7 @@ set(Launcher_LEGACY_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 11) set(Launcher_VERSION_MINOR 0) -set(Launcher_VERSION_PATCH 0) +set(Launcher_VERSION_PATCH 1) set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0") From 960e1bac877c9d37f61c3ccd6c5fa5061c9ba1b0 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 20:53:42 +0500 Subject: [PATCH 02/13] fix(PrintInstanceInfo): add break before OS info Signed-off-by: Octol1ttle (cherry picked from commit 4cf8cf7d18d63b113a0c25e096074679e61f99f9) --- launcher/minecraft/launch/PrintInstanceInfo.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/launcher/minecraft/launch/PrintInstanceInfo.cpp b/launcher/minecraft/launch/PrintInstanceInfo.cpp index d837f5faf..7bfe73746 100644 --- a/launcher/minecraft/launch/PrintInstanceInfo.cpp +++ b/launcher/minecraft/launch/PrintInstanceInfo.cpp @@ -61,6 +61,7 @@ void PrintInstanceInfo::executeTask() auto instance = m_parent->instance(); QStringList log; + log << ""; log << "OS: " + QString("%1 | %2 | %3").arg(QSysInfo::prettyProductName(), QSysInfo::kernelType(), QSysInfo::kernelVersion()); #ifdef Q_OS_FREEBSD ::runSysctlHwModel(log); From 4cd8c343feb2cd9cbe74ac34ab1545533dd0d141 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 20:40:22 +0500 Subject: [PATCH 03/13] fix(CI/nix): bump macOS Signed-off-by: Octol1ttle (cherry picked from commit 724c9a4a2c07d54b188fa6a9f8185442208cf08c) --- .github/workflows/nix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index a2398642f..0fea44f08 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -88,7 +88,7 @@ jobs: - os: ubuntu-22.04-arm system: aarch64-linux - - os: macos-14 + - os: macos-26 system: aarch64-darwin runs-on: ${{ matrix.os }} From f66796e8063692ebfd8f3a3846512f5abfda6905 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 20:11:27 +0500 Subject: [PATCH 04/13] fix: don't count JAR mods when checking offline libraries Signed-off-by: Octol1ttle (cherry picked from commit ec4484282c8af4b3744fdf9ad018b188fc2f0ba8) --- launcher/minecraft/LaunchProfile.cpp | 5 ++-- launcher/minecraft/LaunchProfile.h | 3 ++- .../launch/EnsureOfflineLibraries.cpp | 25 +++++++++++++------ 3 files changed, 23 insertions(+), 10 deletions(-) diff --git a/launcher/minecraft/LaunchProfile.cpp b/launcher/minecraft/LaunchProfile.cpp index 577b8581c..fb74d4a9a 100644 --- a/launcher/minecraft/LaunchProfile.cpp +++ b/launcher/minecraft/LaunchProfile.cpp @@ -349,7 +349,8 @@ void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext, QStringList& jars, QStringList& nativeJars, const QString& overridePath, - const QString& tempPath) const + const QString& tempPath, + bool addJarMods) const { QStringList native32, native64; jars.clear(); @@ -360,7 +361,7 @@ void LaunchProfile::getLibraryFiles(const RuntimeContext& runtimeContext, // NOTE: order is important here, add main jar last to the lists if (m_mainJar) { // FIXME: HACK!! jar modding is weird and unsystematic! - if (m_jarMods.size()) { + if (m_jarMods.size() && addJarMods) { QDir tempDir(tempPath); jars.append(tempDir.absoluteFilePath("minecraft.jar")); } else { diff --git a/launcher/minecraft/LaunchProfile.h b/launcher/minecraft/LaunchProfile.h index 0d2d97c45..6dc3d9aeb 100644 --- a/launcher/minecraft/LaunchProfile.h +++ b/launcher/minecraft/LaunchProfile.h @@ -87,7 +87,8 @@ class LaunchProfile : public ProblemProvider { QStringList& jars, QStringList& nativeJars, const QString& overridePath, - const QString& tempPath) const; + const QString& tempPath, + bool addJarMods = true) const; bool hasTrait(const QString& trait) const; ProblemSeverity getProblemSeverity() const override; const QList getProblems() const override; diff --git a/launcher/minecraft/launch/EnsureOfflineLibraries.cpp b/launcher/minecraft/launch/EnsureOfflineLibraries.cpp index 59801a1d0..0165fbdf9 100644 --- a/launcher/minecraft/launch/EnsureOfflineLibraries.cpp +++ b/launcher/minecraft/launch/EnsureOfflineLibraries.cpp @@ -27,16 +27,27 @@ void EnsureOfflineLibraries::executeTask() { const auto profile = m_instance->getPackProfile()->getProfile(); QStringList allJars; - profile->getLibraryFiles(m_instance->runtimeContext(), allJars, allJars, m_instance->getLocalLibraryPath(), m_instance->binRoot()); + profile->getLibraryFiles(m_instance->runtimeContext(), allJars, allJars, m_instance->getLocalLibraryPath(), m_instance->binRoot(), + false); + + QStringList missing; for (const auto& jar : allJars) { if (!QFileInfo::exists(jar)) { - emit logLine(tr("This instance cannot be launched because some libraries are missing or have not been downloaded yet. Please " - "try again in online mode with a working Internet connection"), - MessageLevel::Fatal); - emitFailed("Required libraries are missing"); - return; + missing.append(jar); } } - emitSucceeded(); + if (missing.isEmpty()) { + emitSucceeded(); + return; + } + + emit logLine("Missing libraries:", MessageLevel::Error); + for (const auto& jar : missing) { + emit logLine(" " + jar, MessageLevel::Error); + } + emit logLine(tr("\nThis instance cannot be launched because some libraries are missing or have not been downloaded yet. Please " + "try again in online mode with a working Internet connection"), + MessageLevel::Fatal); + emitFailed("Required libraries are missing"); } From fe02ad8524b0d9b573e8eab24da7853275f5d768 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 17:41:25 +0500 Subject: [PATCH 05/13] fix(McClient): do not use unsigned type for response length Signed-off-by: Octol1ttle (cherry picked from commit 2fe0569bd6213205949bf5a1f49600958dfe0e24) --- launcher/ui/pages/instance/McClient.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/ui/pages/instance/McClient.h b/launcher/ui/pages/instance/McClient.h index 633e7aaed..f0a6808fa 100644 --- a/launcher/ui/pages/instance/McClient.h +++ b/launcher/ui/pages/instance/McClient.h @@ -20,7 +20,7 @@ class McClient : public QObject { // 1: read the response length, still reading the response // 2: finished reading the response unsigned m_responseReadState = 0; - unsigned m_wantedRespLength = 0; + int32_t m_wantedRespLength = 0; QByteArray m_resp; public: From 09f0467e814dae28e09414a801321a3dc6abac21 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 18:57:33 +0500 Subject: [PATCH 06/13] refactor: McClient Signed-off-by: Octol1ttle (cherry picked from commit 91616ae9b6379d18dec5131870554bfacc5f7543) --- launcher/ui/pages/instance/McClient.cpp | 90 +++++++++++++------------ launcher/ui/pages/instance/McClient.h | 59 ++++++++-------- 2 files changed, 78 insertions(+), 71 deletions(-) diff --git a/launcher/ui/pages/instance/McClient.cpp b/launcher/ui/pages/instance/McClient.cpp index f110e1fe8..0a719431d 100644 --- a/launcher/ui/pages/instance/McClient.cpp +++ b/launcher/ui/pages/instance/McClient.cpp @@ -1,18 +1,17 @@ +#include "McClient.h" + #include #include #include #include +#include -#include +#include "Exception.h" #include "Json.h" -#include "McClient.h" -// 7 first bits -#define SEGMENT_BITS 0x7F -// last bit -#define CONTINUE_BIT 0x80 - -McClient::McClient(QObject* parent, QString domain, QString ip, short port) : QObject(parent), m_domain(domain), m_ip(ip), m_port(port) {} +McClient::McClient(QObject* parent, QString domain, QString ip, const uint16_t port) + : QObject(parent), m_domain(std::move(domain)), m_ip(std::move(ip)), m_port(port) +{} void McClient::getStatusData() { @@ -33,13 +32,12 @@ void McClient::getStatusData() void McClient::sendRequest() { QByteArray data; - writeVarInt(data, 0x00); // packet ID - writeVarInt(data, 763); // hardcoded protocol version (763 = 1.20.1) - writeVarInt(data, m_domain.size()); // server address length - writeString(data, m_domain.toStdString()); // server address - writeFixedInt(data, m_port, 2); // server port - writeVarInt(data, 0x01); // next state - writePacketToSocket(data); // send handshake packet + writeVarInt(data, 0x00); // packet ID + writeVarInt(data, 763); // hardcoded protocol version (763 = 1.20.1) + writeString(data, m_domain); // server address + writeUInt16(data, m_port); // server port + writeVarInt(data, 0x01); // next state + writePacketToSocket(data); // send handshake packet writeVarInt(data, 0x00); // packet ID writePacketToSocket(data); // send status packet @@ -47,17 +45,17 @@ void McClient::sendRequest() void McClient::readRawResponse() { - if (m_responseReadState == 2) { + if (m_responseReadState == ResponseReadState::Finished) { return; } m_resp.append(m_socket.readAll()); - if (m_responseReadState == 0 && m_resp.size() >= 5) { + if (m_responseReadState == ResponseReadState::Waiting && m_resp.size() >= 5) { m_wantedRespLength = readVarInt(m_resp); - m_responseReadState = 1; + m_responseReadState = ResponseReadState::GotLength; } - if (m_responseReadState == 1 && m_resp.size() >= m_wantedRespLength) { + if (m_responseReadState == ResponseReadState::GotLength && m_resp.size() >= m_wantedRespLength) { if (m_resp.size() > m_wantedRespLength) { qDebug().nospace() << "Warning: Packet length doesn't match actual packet size (" << m_wantedRespLength << " expected vs " << m_resp.size() << " received)"; @@ -67,7 +65,7 @@ void McClient::readRawResponse() } catch (const Exception& e) { emitFail(e.cause()); } - m_responseReadState = 2; + m_responseReadState = ResponseReadState::Finished; } } @@ -75,7 +73,7 @@ void McClient::parseResponse() { qDebug() << "Received response successfully"; - int packetID = readVarInt(m_resp); + const int packetID = readVarInt(m_resp); if (packetID != 0x00) { throw Exception(QString("Packet ID doesn't match expected value (0x00 vs 0x%1)").arg(packetID, 0, 16)); } @@ -84,7 +82,7 @@ void McClient::parseResponse() // 'resp' should now be the JSON string QJsonParseError parseError; - QJsonDocument doc = Json::parseUntilGarbage(m_resp, &parseError); + const QJsonDocument doc = Json::parseUntilGarbage(m_resp, &parseError); if (parseError.error != QJsonParseError::NoError) { qDebug() << "Failed to parse JSON:" << parseError.errorString(); emitFail(parseError.errorString()); @@ -93,18 +91,23 @@ void McClient::parseResponse() emitSucceed(doc.object()); } +// NOLINTBEGIN(*-signed-bitwise) + // From https://wiki.vg/Protocol#VarInt_and_VarLong +constexpr uint8_t g_varIntValueMask = 0x7F; +constexpr uint8_t g_varIntContinue = 0x80; + void McClient::writeVarInt(QByteArray& data, int value) { - while ((value & ~SEGMENT_BITS)) { // check if the value is too big to fit in 7 bits + while ((value & ~g_varIntValueMask) != 0) { // check if the value is too big to fit in 7 bits // Write 7 bits - data.append((value & SEGMENT_BITS) | CONTINUE_BIT); + data.append(static_cast((value & ~g_varIntValueMask) | g_varIntContinue)); // NOLINT(*-narrowing-conversions) // Erase theses 7 bits from the value to write // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone value >>= 7; } - data.append(value); + data.append(static_cast(value)); // NOLINT(*-narrowing-conversions) } // From https://wiki.vg/Protocol#VarInt_and_VarLong @@ -112,53 +115,56 @@ int McClient::readVarInt(QByteArray& data) { int value = 0; int position = 0; - char currentByte; while (position < 32) { - currentByte = readByte(data); - value |= (currentByte & SEGMENT_BITS) << position; + const uint8_t currentByte = readByte(data); + value |= (currentByte & g_varIntValueMask) << position; - if ((currentByte & CONTINUE_BIT) == 0) + if ((currentByte & g_varIntContinue) == 0) { break; + } position += 7; } - if (position >= 32) + if (position >= 32) { throw Exception("VarInt is too big"); + } return value; } -char McClient::readByte(QByteArray& data) +// NOLINTEND(*-signed-bitwise) + +uint8_t McClient::readByte(QByteArray& data) { if (data.isEmpty()) { throw Exception("No more bytes to read"); } - char byte = data.at(0); + const uint8_t byte = data.at(0); data.remove(0, 1); return byte; } -// write number with specified size in big endian format -void McClient::writeFixedInt(QByteArray& data, int value, int size) +void McClient::writeUInt16(QByteArray& data, const uint16_t value) { - for (int i = size - 1; i >= 0; i--) { - data.append((value >> (i * 8)) & 0xFF); - } + QDataStream stream(&data, QIODeviceBase::Append); + stream.setByteOrder(QDataStream::BigEndian); + stream << value; } -void McClient::writeString(QByteArray& data, const std::string& value) +void McClient::writeString(QByteArray& data, const QString& value) { - data.append(value.c_str()); + writeVarInt(data, static_cast(value.size())); + data.append(value.toUtf8()); } void McClient::writePacketToSocket(QByteArray& data) { // we prefix the packet with its length QByteArray dataWithSize; - writeVarInt(dataWithSize, data.size()); + writeVarInt(dataWithSize, static_cast(data.size())); dataWithSize.append(data); // write it to the socket @@ -168,7 +174,7 @@ void McClient::writePacketToSocket(QByteArray& data) data.clear(); } -void McClient::emitFail(QString error) +void McClient::emitFail(const QString& error) { qDebug() << "Minecraft server ping for status error:" << error; emit failed(error); @@ -177,6 +183,6 @@ void McClient::emitFail(QString error) void McClient::emitSucceed(QJsonObject data) { - emit succeeded(data); + emit succeeded(std::move(data)); emit finished(); } diff --git a/launcher/ui/pages/instance/McClient.h b/launcher/ui/pages/instance/McClient.h index f0a6808fa..c1cb3d748 100644 --- a/launcher/ui/pages/instance/McClient.h +++ b/launcher/ui/pages/instance/McClient.h @@ -1,53 +1,54 @@ #pragma once + #include -#include #include #include #include -#include - // Client for the Minecraft protocol class McClient : public QObject { Q_OBJECT - QString m_domain; - QString m_ip; - short m_port; - QTcpSocket m_socket; - - // 0: did not start reading the response yet - // 1: read the response length, still reading the response - // 2: finished reading the response - unsigned m_responseReadState = 0; - int32_t m_wantedRespLength = 0; - QByteArray m_resp; - public: - explicit McClient(QObject* parent, QString domain, QString ip, short port); + explicit McClient(QObject* parent, QString domain, QString ip, uint16_t port); //! Read status data of the server, and calls the succeeded() signal with the parsed JSON data void getStatusData(); + signals: + void succeeded(QJsonObject data); + void failed(QString error); + void finished(); + + private: + static uint8_t readByte(QByteArray& data); + static int readVarInt(QByteArray& data); + static void writeUInt16(QByteArray& data, uint16_t value); + static void writeString(QByteArray& data, const QString& value); + static void writeVarInt(QByteArray& data, int value); + private: void sendRequest(); //! Accumulate data until we have a full response, then call parseResponse() once void readRawResponse(); void parseResponse(); - - void writeVarInt(QByteArray& data, int value); - int readVarInt(QByteArray& data); - char readByte(QByteArray& data); - //! write number with specified size in big endian format - void writeFixedInt(QByteArray& data, int value, int size); - void writeString(QByteArray& data, const std::string& value); - void writePacketToSocket(QByteArray& data); - void emitFail(QString error); + void emitFail(const QString& error); void emitSucceed(QJsonObject data); - signals: - void succeeded(QJsonObject data); - void failed(QString error); - void finished(); + private: + enum class ResponseReadState : uint8_t { + Waiting, + GotLength, + Finished + }; + + QString m_domain; + QString m_ip; + uint16_t m_port; + QTcpSocket m_socket; + + ResponseReadState m_responseReadState = ResponseReadState::Waiting; + int32_t m_wantedRespLength = 0; + QByteArray m_resp; }; From 789c65646328ccf7748ef039b29844d4b708685f Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 19:34:11 +0500 Subject: [PATCH 07/13] feat: allow disabling low RAM warning Signed-off-by: Octol1ttle (cherry picked from commit c044ed36af1ebb969b1b8d4a3ef80895ec681921) --- launcher/Application.cpp | 6 + launcher/minecraft/MinecraftInstance.cpp | 1 + .../launch/EnsureAvailableMemory.cpp | 29 +- launcher/ui/widgets/JavaSettingsWidget.cpp | 3 + launcher/ui/widgets/JavaSettingsWidget.ui | 316 +++++++++--------- 5 files changed, 189 insertions(+), 166 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index 7af39b55e..e7acc9038 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -736,6 +736,12 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::defaultMaxJvmMem()); m_settings->registerSetting("PermGen", 128); + // https://github.com/PrismLauncher/PrismLauncher/issues/5305 + // arm64 Macs have configurations with very little physical memory, but the OS can compensate by compressing and swapping + // According to user reports, this is not noticeable under normal usage due to fast storage speeds + const bool lowMemWarningDefault = !(SysInfo::currentSystem() == "osx" && SysInfo::useQTForArch() == "arm64"); + m_settings->registerSetting("LowMemWarning", lowMemWarningDefault); + // Java Settings m_settings->registerSetting("JavaPath", ""); m_settings->registerSetting("JavaSignature", ""); diff --git a/launcher/minecraft/MinecraftInstance.cpp b/launcher/minecraft/MinecraftInstance.cpp index 2848f1e99..e8fc642fa 100644 --- a/launcher/minecraft/MinecraftInstance.cpp +++ b/launcher/minecraft/MinecraftInstance.cpp @@ -209,6 +209,7 @@ void MinecraftInstance::loadSpecificSettings() m_settings->registerOverride(global_settings->getSetting("MinMemAlloc"), memorySetting); m_settings->registerOverride(global_settings->getSetting("MaxMemAlloc"), memorySetting); m_settings->registerOverride(global_settings->getSetting("PermGen"), memorySetting); + m_settings->registerOverride(global_settings->getSetting("LowMemWarning"), memorySetting); // Native library workarounds auto nativeLibraryWorkaroundsOverride = m_settings->registerSetting("OverrideNativeWorkarounds", false); diff --git a/launcher/minecraft/launch/EnsureAvailableMemory.cpp b/launcher/minecraft/launch/EnsureAvailableMemory.cpp index 8a436a6f6..ae0ffe1ef 100644 --- a/launcher/minecraft/launch/EnsureAvailableMemory.cpp +++ b/launcher/minecraft/launch/EnsureAvailableMemory.cpp @@ -31,20 +31,25 @@ void EnsureAvailableMemory::executeTask() const uint64_t required = std::max(min, max); if (required > available) { - auto* dialog = CustomMessageBox::selectable( - nullptr, tr("Not enough RAM"), - tr("There is not enough RAM available to launch this instance with the current memory settings.\n\n" - "Required: %1 MiB\nAvailable: %2 MiB\n\n" - "Continue anyway? This may cause slowdowns in the game and your system.") - .arg(required) - .arg(available), - QMessageBox::Icon::Warning, QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, - QMessageBox::StandardButton::No); - const auto response = dialog->exec(); - dialog->deleteLater(); + bool shouldAbort = false; + + if (m_instance->settings()->get("LowMemWarning").toBool()) { + auto* dialog = CustomMessageBox::selectable( + nullptr, tr("Not enough RAM"), + tr("There is not enough RAM available to launch this instance with the current memory settings.\n\n" + "Required: %1 MiB\nAvailable: %2 MiB\n\n" + "Continue anyway? This may cause slowdowns in the game and your system.") + .arg(required) + .arg(available), + QMessageBox::Icon::Warning, QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::No, + QMessageBox::StandardButton::No); + + shouldAbort = dialog->exec() == QMessageBox::No; + dialog->deleteLater(); + } const auto message = tr("Not enough RAM available to launch this instance"); - if (response == QMessageBox::No) { + if (shouldAbort) { emit logLine(message, MessageLevel::Fatal); emitFailed(message); return; diff --git a/launcher/ui/widgets/JavaSettingsWidget.cpp b/launcher/ui/widgets/JavaSettingsWidget.cpp index 4e74c610f..e13c847d0 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.cpp +++ b/launcher/ui/widgets/JavaSettingsWidget.cpp @@ -151,6 +151,7 @@ void JavaSettingsWidget::loadSettings() m_ui->maxMemSpinBox->setValue(min); } m_ui->permGenSpinBox->setValue(settings->get("PermGen").toInt()); + m_ui->lowMemWarningCheckBox->setChecked(settings->get("LowMemWarning").toBool()); // Java arguments m_ui->javaArgumentsGroupBox->setChecked(m_instance == nullptr || settings->get("OverrideJavaArgs").toBool()); @@ -205,10 +206,12 @@ void JavaSettingsWidget::saveSettings() settings->set("MaxMemAlloc", min); } settings->set("PermGen", m_ui->permGenSpinBox->value()); + settings->set("LowMemWarning", m_ui->lowMemWarningCheckBox->isChecked()); } else { settings->reset("MinMemAlloc"); settings->reset("MaxMemAlloc"); settings->reset("PermGen"); + settings->reset("LowMemWarning"); } // Java arguments diff --git a/launcher/ui/widgets/JavaSettingsWidget.ui b/launcher/ui/widgets/JavaSettingsWidget.ui index 46f714b76..14638cf4e 100644 --- a/launcher/ui/widgets/JavaSettingsWidget.ui +++ b/launcher/ui/widgets/JavaSettingsWidget.ui @@ -55,7 +55,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -86,7 +86,7 @@ - Qt::Horizontal + Qt::Orientation::Horizontal @@ -101,10 +101,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Fixed + QSizePolicy::Policy::Fixed @@ -160,10 +160,10 @@ - Qt::Vertical + Qt::Orientation::Vertical - QSizePolicy::Fixed + QSizePolicy::Policy::Fixed @@ -190,156 +190,166 @@ 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) + + + + + + + + + - (-XX:PermSize) + Warn when there is not enough memory available - - - - - 0 - 0 - - - - The amount of memory available to store loaded Java classes. - - - MiB - - - 4 - - - 1048576 - - - 8 - - - 64 - - - - - - - - 0 - 0 - - - - The maximum amount of memory Minecraft is allowed to use. - - - MiB - - - 8 - - - 1048576 - - - 128 - - - 1024 - - - - - - - (-Xmx) - - - - - - - - 0 - 0 - - - - The amount of memory Minecraft is started with. - - - MiB - - - 8 - - - 1048576 - - - 128 - - - 256 - - - - - - - &PermGen Size: - - - permGenSpinBox - - - - - - - (-Xms) - - - - - - - Ma&ximum Memory Usage: - - - maxMemSpinBox - - - - - - - M&inimum Memory Usage: - - - minMemSpinBox - - - - - - - Qt::Horizontal - - - - 0 - 0 - - - - - + Memory Notice @@ -382,9 +392,7 @@ autodownloadJavaCheckBox javaTestBtn javaDownloadBtn - minMemSpinBox maxMemSpinBox - permGenSpinBox jvmArgsTextBox From f766cdd847e6943d77386dd6da00f7681cfd2492 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Thu, 9 Apr 2026 22:35:32 +0500 Subject: [PATCH 08/13] change(EnsureAvailableMemory): add lenience Signed-off-by: Octol1ttle (cherry picked from commit 658a1391f8fbcf7b51ffc741d393728768b4a5b1) --- launcher/minecraft/launch/EnsureAvailableMemory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/launcher/minecraft/launch/EnsureAvailableMemory.cpp b/launcher/minecraft/launch/EnsureAvailableMemory.cpp index ae0ffe1ef..a0e156770 100644 --- a/launcher/minecraft/launch/EnsureAvailableMemory.cpp +++ b/launcher/minecraft/launch/EnsureAvailableMemory.cpp @@ -30,7 +30,7 @@ void EnsureAvailableMemory::executeTask() const uint64_t max = m_instance->settings()->get("MaxMemAlloc").toUInt(); const uint64_t required = std::max(min, max); - if (required > available) { + if (static_cast(required) * 0.9 > static_cast(available)) { bool shouldAbort = false; if (m_instance->settings()->get("LowMemWarning").toBool()) { From fb7e4da4e62a4928d87bb0bca18418534c52059c Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Fri, 10 Apr 2026 11:53:37 +0500 Subject: [PATCH 09/13] Change LowMemWarning default to always enabled Signed-off-by: Octol1ttle (cherry picked from commit 4b3aedd5d0567b57856453c96250637cf1495f67) --- launcher/Application.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/launcher/Application.cpp b/launcher/Application.cpp index e7acc9038..115b6489c 100644 --- a/launcher/Application.cpp +++ b/launcher/Application.cpp @@ -735,12 +735,7 @@ Application::Application(int& argc, char** argv) : QApplication(argc, argv) m_settings->registerSetting({ "MinMemAlloc", "MinMemoryAlloc" }, 512); m_settings->registerSetting({ "MaxMemAlloc", "MaxMemoryAlloc" }, SysInfo::defaultMaxJvmMem()); m_settings->registerSetting("PermGen", 128); - - // https://github.com/PrismLauncher/PrismLauncher/issues/5305 - // arm64 Macs have configurations with very little physical memory, but the OS can compensate by compressing and swapping - // According to user reports, this is not noticeable under normal usage due to fast storage speeds - const bool lowMemWarningDefault = !(SysInfo::currentSystem() == "osx" && SysInfo::useQTForArch() == "arm64"); - m_settings->registerSetting("LowMemWarning", lowMemWarningDefault); + m_settings->registerSetting("LowMemWarning", true); // Java Settings m_settings->registerSetting("JavaPath", ""); From 239be1ec433bb480b8fcce99e29c52345f794a3d Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 Apr 2026 00:36:05 +0300 Subject: [PATCH 10/13] fix pack upgrade Signed-off-by: Trial97 (cherry picked from commit b7344af313397c00a80fc823ecfa46229eec1ff0) --- launcher/InstanceList.cpp | 12 +++++------- launcher/ui/pages/instance/ManagedPackPage.cpp | 15 ++++++++------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/launcher/InstanceList.cpp b/launcher/InstanceList.cpp index 1fe96144d..1339499c7 100644 --- a/launcher/InstanceList.cpp +++ b/launcher/InstanceList.cpp @@ -924,23 +924,23 @@ class InstanceStaging : public Task { connect(child, &Task::progress, this, &InstanceStaging::setProgress); connect(child, &Task::stepProgress, this, &InstanceStaging::propagateStepProgress); connect(&m_backoffTimer, &QTimer::timeout, this, &InstanceStaging::childSucceeded); - m_backoffTimer.setSingleShot(true); } - virtual ~InstanceStaging() {} + ~InstanceStaging() override = default; // FIXME/TODO: add ability to abort during instance commit retries bool abort() override { - if (!canAbort()) + if (!canAbort()) { return false; + } return m_child->abort(); } bool canAbort() const override { return (m_child && m_child->canAbort()); } protected: - virtual void executeTask() override + void executeTask() override { if (m_stagingPath.isNull()) { emitFailed(tr("Could not create staging folder")); @@ -954,10 +954,8 @@ class InstanceStaging : public Task { private slots: void childSucceeded() { - if (!isRunning()) - return; unsigned sleepTime = backoff(); - if (m_parent->commitStagedInstance(m_stagingPath, *m_child.get(), m_child->group(), *m_child.get())) { + if (m_parent->commitStagedInstance(m_stagingPath, *m_child, m_child->group(), *m_child)) { m_backoffTimer.stop(); emitSucceeded(); return; diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index ad6b39723..508bfeba0 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -202,23 +202,24 @@ bool ManagedPackPage::runUpdateTask(InstanceTask* task) unique_qobject_ptr wrapped_task(APPLICATION->instances()->wrapInstanceTask(task)); - connect(task, &Task::failed, - [this](QString reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); - connect(task, &Task::succeeded, [this, task]() { + connect(wrapped_task.get(), &Task::failed, + [this](const QString& reason) { CustomMessageBox::selectable(this, tr("Error"), reason, QMessageBox::Critical)->show(); }); + connect(wrapped_task.get(), &Task::succeeded, [this, task]() { QStringList warnings = task->warnings(); - if (warnings.count()) + if (warnings.count()) { CustomMessageBox::selectable(this, tr("Warnings"), warnings.join('\n'), QMessageBox::Warning)->show(); + } }); - connect(task, &Task::aborted, [this] { + connect(wrapped_task.get(), &Task::aborted, [this] { CustomMessageBox::selectable(this, tr("Task aborted"), tr("The task has been aborted by the user."), QMessageBox::Information) ->show(); }); ProgressDialog loadDialog(this); loadDialog.setSkipButton(true, tr("Abort")); - loadDialog.execWithTask(task); + loadDialog.execWithTask(wrapped_task.get()); - return task->wasSuccessful(); + return wrapped_task->wasSuccessful(); } void ManagedPackPage::suggestVersion() From 6d38b34c004acb4faf777cf2f5ae86aa58e9c267 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Fri, 10 Apr 2026 19:29:56 +0300 Subject: [PATCH 11/13] enable modpack changelog for modrinth page Signed-off-by: Trial97 (cherry picked from commit f3ff0a730a3cc220240405c6518e0fda2dd9cf3b) --- .../ui/pages/instance/ManagedPackPage.cpp | 42 ++++++++++++------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/launcher/ui/pages/instance/ManagedPackPage.cpp b/launcher/ui/pages/instance/ManagedPackPage.cpp index 508bfeba0..777dfbaa1 100644 --- a/launcher/ui/pages/instance/ManagedPackPage.cpp +++ b/launcher/ui/pages/instance/ManagedPackPage.cpp @@ -261,14 +261,16 @@ void ModrinthManagedPackPage::parseManagedPack() qDebug() << "Parsing Modrinth pack"; // No need for the extra work because we already have everything we need. - if (m_loaded) + if (m_loaded) { return; + } - if (m_fetch_job && m_fetch_job->isRunning()) + if (m_fetch_job && m_fetch_job->isRunning()) { m_fetch_job->abort(); + } ResourceAPI::Callback> callbacks{}; - m_pack = { m_inst->getManagedPackID() }; + m_pack = { .addonId = m_inst->getManagedPackID() }; // Use default if no callbacks are set callbacks.on_succeed = [this](auto& doc) { @@ -285,8 +287,9 @@ void ModrinthManagedPackPage::parseManagedPack() // NOTE: the id from version isn't the same id in the modpack format spec... // e.g. HexMC's 4.4.0 has versionId 4.0.0 in the modpack index.............. - if (version.version == m_inst->getManagedPackVersionName()) + if (version.version == m_inst->getManagedPackVersionName()) { name = tr("%1 (Current)").arg(name); + } ui->versionsComboBox->addItem(name, version.fileId); } @@ -295,10 +298,14 @@ void ModrinthManagedPackPage::parseManagedPack() m_loaded = true; }; - callbacks.on_fail = [this](QString reason, int) { setFailState(); }; + callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); }; callbacks.on_abort = [this]() { setFailState(); }; - m_fetch_job = m_api.getProjectVersions( - { std::make_shared(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks)); + m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared(m_pack), + .mcVersions = {}, + .loaders = {}, + .resourceType = ModPlatform::ResourceType::Modpack, + .includeChangelog = true }, + std::move(callbacks)); ui->changelogTextBrowser->setText(tr("Fetching changelogs...")); @@ -407,14 +414,16 @@ void FlameManagedPackPage::parseManagedPack() } // No need for the extra work because we already have everything we need. - if (m_loaded) + if (m_loaded) { return; + } - if (m_fetch_job && m_fetch_job->isRunning()) + if (m_fetch_job && m_fetch_job->isRunning()) { m_fetch_job->abort(); + } QString id = m_inst->getManagedPackID(); - m_pack = { id }; + m_pack = { .addonId = id }; ResourceAPI::Callback> callbacks{}; @@ -431,8 +440,9 @@ void FlameManagedPackPage::parseManagedPack() for (const auto& version : m_pack.versions) { QString name = version.getVersionDisplayString(); - if (version.fileId == m_inst->getManagedPackVersionID().toInt()) + if (version.fileId == m_inst->getManagedPackVersionID().toInt()) { name = tr("%1 (Current)").arg(name); + } ui->versionsComboBox->addItem(name, QVariant(version.fileId)); } @@ -441,10 +451,14 @@ void FlameManagedPackPage::parseManagedPack() m_loaded = true; }; - callbacks.on_fail = [this](QString reason, int) { setFailState(); }; + callbacks.on_fail = [this](const QString& /*reason*/, int) { setFailState(); }; callbacks.on_abort = [this]() { setFailState(); }; - m_fetch_job = m_api.getProjectVersions( - { std::make_shared(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks)); + m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared(m_pack), + .mcVersions = {}, + .loaders = {}, + .resourceType = ModPlatform::ResourceType::Modpack, + .includeChangelog = true }, + std::move(callbacks)); m_fetch_job->start(); } From add3d01f841b48beab136a2acfa3047c10ceb008 Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Sun, 12 Apr 2026 13:02:46 +0500 Subject: [PATCH 12/13] fix(updater): do not reset current task in finished signal The order of signals in case of a success is "succeeded"->"finished" The "succeeded" signal may launch another download if the updater needs to fetch more pages But if we reset the task then the newly started download will be disposed and the updater will softlock Signed-off-by: Octol1ttle (cherry picked from commit 9b270f783ee1c54b77917fde76ec8efd3f84b2e9) --- launcher/updater/prismupdater/PrismUpdater.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/launcher/updater/prismupdater/PrismUpdater.cpp b/launcher/updater/prismupdater/PrismUpdater.cpp index 11e92efcd..6ad60a7c7 100644 --- a/launcher/updater/prismupdater/PrismUpdater.cpp +++ b/launcher/updater/prismupdater/PrismUpdater.cpp @@ -1160,8 +1160,6 @@ void PrismUpdaterApp::downloadReleasePage(const QString& api_url, int page) m_current_task.reset(download); connect(download.get(), &Net::Download::finished, this, [this]() { qDebug() << "Download" << m_current_task->getUid().toString() << "finished"; - m_current_task.reset(); - m_current_url = ""; }); QCoreApplication::processEvents(); From 399482270fad9a93f66f4279e1ca6039e5920bbb Mon Sep 17 00:00:00 2001 From: Octol1ttle Date: Sun, 12 Apr 2026 20:35:15 +0500 Subject: [PATCH 13/13] chore: bump version Signed-off-by: Octol1ttle --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e5fe090b..6b0d41741 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,7 @@ set(Launcher_LEGACY_FMLLIBS_BASE_URL "https://files.prismlauncher.org/fmllibs/" ######## Set version numbers ######## set(Launcher_VERSION_MAJOR 11) set(Launcher_VERSION_MINOR 0) -set(Launcher_VERSION_PATCH 1) +set(Launcher_VERSION_PATCH 2) set(Launcher_VERSION_NAME "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}") set(Launcher_VERSION_NAME4 "${Launcher_VERSION_MAJOR}.${Launcher_VERSION_MINOR}.${Launcher_VERSION_PATCH}.0")