Compare commits

...

19 commits

Author SHA1 Message Date
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
15 changed files with 330 additions and 278 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 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")

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