Compare commits

...

23 commits

Author SHA1 Message Date
Alexandru Ionut Tripon 9a9e8573aa
[release-11.x] chore: bump version (#5376) 2026-04-12 18:38:53 +03:00
Octol1ttle 399482270f
chore: bump version
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
2026-04-12 20:35:15 +05:00
Alexandru Ionut Tripon 50df409b28
[Backport release-11.x] Updater: Do not reset current task in finished signal (#5372) 2026-04-12 11:52:52 +03:00
Octol1ttle add3d01f84 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 <l1ttleofficial@outlook.com>
(cherry picked from commit 9b270f783e)
2026-04-12 08:40:20 +00:00
Alexandru Ionut Tripon a71b8d8fe3
[Backport release-11.x] enable modpack changelog for modrinth page (#5360) 2026-04-11 09:36:29 +03:00
Trial97 6d38b34c00 enable modpack changelog for modrinth page
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit f3ff0a730a)
2026-04-11 05:37:04 +00:00
Alexandru Ionut Tripon 85f19da603
[Backport release-11.x] fix pack upgrade (#5356) 2026-04-10 20:32:09 +03:00
Trial97 239be1ec43 fix pack upgrade
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
(cherry picked from commit b7344af313)
2026-04-10 17:31:48 +00:00
Alexandru Ionut Tripon 34349a6810
[Backport release-11.x] Allow disabling low RAM warning (#5350) 2026-04-10 15:23:20 +03:00
Octol1ttle fb7e4da4e6 Change LowMemWarning default to always enabled
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 4b3aedd5d0)
2026-04-10 09:31:16 +00:00
Octol1ttle f766cdd847 change(EnsureAvailableMemory): add lenience
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 658a1391f8)
2026-04-10 09:31:16 +00:00
Octol1ttle 789c656463 feat: allow disabling low RAM warning
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit c044ed36af)
2026-04-10 09:31:16 +00:00
Alexandru Ionut Tripon 2d01dfa4f2
[Backport release-11.x] fix McClient (#5344) 2026-04-10 00:50:18 +03:00
Alexandru Ionut Tripon 9231fe8592
[Backport release-11.x] Don't count JAR mods when checking offline libraries (#5343) 2026-04-10 00:49:58 +03:00
Alexandru Ionut Tripon 25fee30f95
[Backport release-11.x] CI/Nix: Bump macOS (#5342) 2026-04-10 00:49:42 +03:00
Alexandru Ionut Tripon ec6a173608
[Backport release-11.x] fix(PrintInstanceInfo): add break before OS info (#5341) 2026-04-10 00:49:24 +03:00
Alexandru Ionut Tripon b7d70dc1c1
chore: bump to 11.0.1 (#5340) 2026-04-10 00:49:09 +03:00
Octol1ttle 09f0467e81 refactor: McClient
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 91616ae9b6)
2026-04-09 21:06:40 +00:00
Octol1ttle fe02ad8524 fix(McClient): do not use unsigned type for response length
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 2fe0569bd6)
2026-04-09 21:06:40 +00:00
Octol1ttle f66796e806 fix: don't count JAR mods when checking offline libraries
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit ec4484282c)
2026-04-09 20:56:32 +00:00
Octol1ttle 4cd8c343fe fix(CI/nix): bump macOS
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 724c9a4a2c)
2026-04-09 20:47:44 +00:00
Octol1ttle 960e1bac87 fix(PrintInstanceInfo): add break before OS info
Signed-off-by: Octol1ttle <l1ttleofficial@outlook.com>
(cherry picked from commit 4cf8cf7d18)
2026-04-09 20:46:36 +00:00
Trial97 838e7fb8d2 chore: bump to 11.0.1
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2026-04-09 23:33:42 +03:00
16 changed files with 330 additions and 280 deletions

View file

@ -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 }}

View file

@ -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 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")

View file

@ -735,6 +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);
m_settings->registerSetting("LowMemWarning", true);
// Java Settings
m_settings->registerSetting("JavaPath", "");

View file

@ -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;

View file

@ -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 {

View file

@ -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<PatchProblem> getProblems() const override;

View file

@ -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);

View file

@ -30,21 +30,26 @@ void EnsureAvailableMemory::executeTask()
const uint64_t max = m_instance->settings()->get("MaxMemAlloc").toUInt();
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();
if (static_cast<double>(required) * 0.9 > static_cast<double>(available)) {
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;

View file

@ -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");
}

View file

@ -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);

View file

@ -202,23 +202,24 @@ bool ManagedPackPage::runUpdateTask(InstanceTask* task)
unique_qobject_ptr<Task> 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()
@ -260,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<QVector<ModPlatform::IndexedVersion>> 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) {
@ -284,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);
}
@ -294,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<ModPlatform::IndexedPack>(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
.mcVersions = {},
.loaders = {},
.resourceType = ModPlatform::ResourceType::Modpack,
.includeChangelog = true },
std::move(callbacks));
ui->changelogTextBrowser->setText(tr("Fetching changelogs..."));
@ -406,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<QVector<ModPlatform::IndexedVersion>> callbacks{};
@ -430,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));
}
@ -440,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<ModPlatform::IndexedPack>(m_pack), {}, {}, ModPlatform::ResourceType::Modpack }, std::move(callbacks));
m_fetch_job = m_api.getProjectVersions({ .pack = std::make_shared<ModPlatform::IndexedPack>(m_pack),
.mcVersions = {},
.loaders = {},
.resourceType = ModPlatform::ResourceType::Modpack,
.includeChangelog = true },
std::move(callbacks));
m_fetch_job->start();
}

View file

@ -1,18 +1,17 @@
#include "McClient.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QObject>
#include <QTcpSocket>
#include <utility>
#include <Exception.h>
#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<uint8_t>((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<uint8_t>(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<int32_t>(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<int32_t>(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();
}

View file

@ -1,53 +1,54 @@
#pragma once
#include <QFuture>
#include <QJsonDocument>
#include <QJsonObject>
#include <QObject>
#include <QTcpSocket>
#include <Exception.h>
// 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;
unsigned 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;
};

View file

@ -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

View file

@ -55,7 +55,7 @@
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -86,7 +86,7 @@
<item>
<spacer name="horizontalSpacer_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<enum>Qt::Orientation::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -101,10 +101,10 @@
<item row="9" column="0">
<spacer name="verticalSpacer_4">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -160,10 +160,10 @@
<item row="3" column="0">
<spacer name="verticalSpacer_5">
<property name="orientation">
<enum>Qt::Vertical</enum>
<enum>Qt::Orientation::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
<enum>QSizePolicy::Policy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
@ -190,156 +190,166 @@
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="2">
<widget class="QLabel" name="label_3">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="labelMinMem">
<property name="text">
<string>M&amp;inimum Memory Usage:</string>
</property>
<property name="buddy">
<cstring>minMemSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QSpinBox" name="minMemSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The amount of memory Minecraft is started with.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
<number>256</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>(-Xms)</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelMaxMem">
<property name="text">
<string>Ma&amp;ximum Memory Usage:</string>
</property>
<property name="buddy">
<cstring>maxMemSpinBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QSpinBox" name="maxMemSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The maximum amount of memory Minecraft is allowed to use.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
<number>1024</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>(-Xmx)</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>&amp;PermGen Size:</string>
</property>
<property name="buddy">
<cstring>permGenSpinBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QSpinBox" name="permGenSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The amount of memory available to store loaded Java classes.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>4</number>
</property>
<property name="maximum">
<number>1048576</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
<property name="value">
<number>64</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>(-XX:PermSize)</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="lowMemWarningCheckBox">
<property name="text">
<string>(-XX:PermSize)</string>
<string>Warn when there is not enough memory available</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="permGenSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The amount of memory available to store loaded Java classes.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>4</number>
</property>
<property name="maximum">
<number>1048576</number>
</property>
<property name="singleStep">
<number>8</number>
</property>
<property name="value">
<number>64</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QSpinBox" name="maxMemSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The maximum amount of memory Minecraft is allowed to use.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
<number>1024</number>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>(-Xmx)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QSpinBox" name="minMemSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>The amount of memory Minecraft is started with.</string>
</property>
<property name="suffix">
<string notr="true"> MiB</string>
</property>
<property name="minimum">
<number>8</number>
</property>
<property name="maximum">
<number>1048576</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
<number>256</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>&amp;PermGen Size:</string>
</property>
<property name="buddy">
<cstring>permGenSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="label">
<property name="text">
<string>(-Xms)</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="labelMaxMem">
<property name="text">
<string>Ma&amp;ximum Memory Usage:</string>
</property>
<property name="buddy">
<cstring>maxMemSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="labelMinMem">
<property name="text">
<string>M&amp;inimum Memory Usage:</string>
</property>
<property name="buddy">
<cstring>minMemSpinBox</cstring>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0" colspan="4">
<item>
<widget class="QLabel" name="labelMaxMemNotice">
<property name="text">
<string>Memory Notice</string>
@ -382,9 +392,7 @@
<tabstop>autodownloadJavaCheckBox</tabstop>
<tabstop>javaTestBtn</tabstop>
<tabstop>javaDownloadBtn</tabstop>
<tabstop>minMemSpinBox</tabstop>
<tabstop>maxMemSpinBox</tabstop>
<tabstop>permGenSpinBox</tabstop>
<tabstop>jvmArgsTextBox</tabstop>
</tabstops>
<resources/>

View file

@ -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();