From d4b522b6cb5281df02da54cd9e0f6445770e7ec7 Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Wed, 12 Jan 2022 10:36:26 +0100
Subject: [PATCH 01/45] Add offline mode UI
---
launcher/CMakeLists.txt | 3 +
launcher/ui/dialogs/OfflineLoginDialog.cpp | 113 +++++++++++++++++++
launcher/ui/dialogs/OfflineLoginDialog.h | 58 ++++++++++
launcher/ui/dialogs/OfflineLoginDialog.ui | 67 +++++++++++
launcher/ui/pages/global/AccountListPage.cpp | 17 +++
launcher/ui/pages/global/AccountListPage.h | 1 +
launcher/ui/pages/global/AccountListPage.ui | 6 +
7 files changed, 265 insertions(+)
create mode 100644 launcher/ui/dialogs/OfflineLoginDialog.cpp
create mode 100644 launcher/ui/dialogs/OfflineLoginDialog.h
create mode 100644 launcher/ui/dialogs/OfflineLoginDialog.ui
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index b5c52afa2..0052f0e22 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -769,6 +769,8 @@ SET(LAUNCHER_SOURCES
ui/dialogs/LoginDialog.h
ui/dialogs/MSALoginDialog.cpp
ui/dialogs/MSALoginDialog.h
+ ui/dialogs/OfflineLoginDialog.cpp
+ ui/dialogs/OfflineLoginDialog.h
ui/dialogs/NewComponentDialog.cpp
ui/dialogs/NewComponentDialog.h
ui/dialogs/NewInstanceDialog.cpp
@@ -880,6 +882,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/dialogs/ExportInstanceDialog.ui
ui/dialogs/IconPickerDialog.ui
ui/dialogs/MSALoginDialog.ui
+ ui/dialogs/OfflineLoginDialog.ui
ui/dialogs/AboutDialog.ui
ui/dialogs/LoginDialog.ui
ui/dialogs/EditAccountDialog.ui
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp
new file mode 100644
index 000000000..f6ecc4e97
--- /dev/null
+++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp
@@ -0,0 +1,113 @@
+/* Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "OfflineLoginDialog.h"
+#include "ui_OfflineLoginDialog.h"
+
+#include "minecraft/auth/AccountTask.h"
+
+#include
+
+OfflineLoginDialog::OfflineLoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::OfflineLoginDialog)
+{
+ ui->setupUi(this);
+ ui->progressBar->setVisible(false);
+ ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
+
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+ connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+}
+
+OfflineLoginDialog::~OfflineLoginDialog()
+{
+ delete ui;
+}
+
+// Stage 1: User interaction
+void OfflineLoginDialog::accept()
+{
+ setUserInputsEnabled(false);
+ ui->progressBar->setVisible(true);
+
+ // Setup the login task and start it
+ m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
+ m_loginTask = m_account->login("TODO: create offline mode account flow");
+ connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed);
+ connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded);
+ connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus);
+ connect(m_loginTask.get(), &Task::progress, this, &OfflineLoginDialog::onTaskProgress);
+ m_loginTask->start();
+}
+
+void OfflineLoginDialog::setUserInputsEnabled(bool enable)
+{
+ ui->userTextBox->setEnabled(enable);
+ ui->buttonBox->setEnabled(enable);
+}
+
+// Enable the OK button only when the textbox contains something.
+void OfflineLoginDialog::on_userTextBox_textEdited(const QString &newText)
+{
+ ui->buttonBox->button(QDialogButtonBox::Ok)
+ ->setEnabled(!newText.isEmpty());
+}
+
+void OfflineLoginDialog::onTaskFailed(const QString &reason)
+{
+ // Set message
+ auto lines = reason.split('\n');
+ QString processed;
+ for(auto line: lines) {
+ if(line.size()) {
+ processed += "" + line + "
";
+ }
+ else {
+ processed += "
";
+ }
+ }
+ ui->label->setText(processed);
+
+ // Re-enable user-interaction
+ setUserInputsEnabled(true);
+ ui->progressBar->setVisible(false);
+}
+
+void OfflineLoginDialog::onTaskSucceeded()
+{
+ QDialog::accept();
+}
+
+void OfflineLoginDialog::onTaskStatus(const QString &status)
+{
+ ui->label->setText(status);
+}
+
+void OfflineLoginDialog::onTaskProgress(qint64 current, qint64 total)
+{
+ ui->progressBar->setMaximum(total);
+ ui->progressBar->setValue(current);
+}
+
+// Public interface
+MinecraftAccountPtr OfflineLoginDialog::newAccount(QWidget *parent, QString msg)
+{
+ OfflineLoginDialog dlg(parent);
+ dlg.ui->label->setText(msg);
+ if (dlg.exec() == QDialog::Accepted)
+ {
+ return dlg.m_account;
+ }
+ return 0;
+}
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.h b/launcher/ui/dialogs/OfflineLoginDialog.h
new file mode 100644
index 000000000..ba0c18232
--- /dev/null
+++ b/launcher/ui/dialogs/OfflineLoginDialog.h
@@ -0,0 +1,58 @@
+/* Copyright 2013-2021 MultiMC Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include
+#include
+
+#include "minecraft/auth/MinecraftAccount.h"
+#include "tasks/Task.h"
+
+namespace Ui
+{
+class OfflineLoginDialog;
+}
+
+class OfflineLoginDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ ~OfflineLoginDialog();
+
+ static MinecraftAccountPtr newAccount(QWidget *parent, QString message);
+
+private:
+ explicit OfflineLoginDialog(QWidget *parent = 0);
+
+ void setUserInputsEnabled(bool enable);
+
+protected
+slots:
+ void accept();
+
+ void onTaskFailed(const QString &reason);
+ void onTaskSucceeded();
+ void onTaskStatus(const QString &status);
+ void onTaskProgress(qint64 current, qint64 total);
+
+ void on_userTextBox_textEdited(const QString &newText);
+
+private:
+ Ui::OfflineLoginDialog *ui;
+ MinecraftAccountPtr m_account;
+ Task::Ptr m_loginTask;
+};
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.ui b/launcher/ui/dialogs/OfflineLoginDialog.ui
new file mode 100644
index 000000000..d8964a2e4
--- /dev/null
+++ b/launcher/ui/dialogs/OfflineLoginDialog.ui
@@ -0,0 +1,67 @@
+
+
+ OfflineLoginDialog
+
+
+
+ 0
+ 0
+ 400
+ 150
+
+
+
+
+ 0
+ 0
+
+
+
+ Add Account
+
+
+ -
+
+
+ Message label placeholder.
+
+
+ Qt::RichText
+
+
+ Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+
+ -
+
+
+ Username
+
+
+
+ -
+
+
+ 69
+
+
+ false
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp
index b8da6c754..b9aa7628b 100644
--- a/launcher/ui/pages/global/AccountListPage.cpp
+++ b/launcher/ui/pages/global/AccountListPage.cpp
@@ -24,6 +24,7 @@
#include "net/NetJob.h"
#include "ui/dialogs/ProgressDialog.h"
+#include "ui/dialogs/OfflineLoginDialog.h"
#include "ui/dialogs/LoginDialog.h"
#include "ui/dialogs/MSALoginDialog.h"
#include "ui/dialogs/CustomMessageBox.h"
@@ -153,6 +154,22 @@ void AccountListPage::on_actionAddMicrosoft_triggered()
}
}
+void AccountListPage::on_actionAddOffline_triggered()
+{
+ MinecraftAccountPtr account = OfflineLoginDialog::newAccount(
+ this,
+ tr("Please enter your desired username to add your offline account.")
+ );
+
+ if (account)
+ {
+ m_accounts->addAccount(account);
+ if (m_accounts->count() == 1) {
+ m_accounts->setDefaultAccount(account);
+ }
+ }
+}
+
void AccountListPage::on_actionRemove_triggered()
{
QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes();
diff --git a/launcher/ui/pages/global/AccountListPage.h b/launcher/ui/pages/global/AccountListPage.h
index 1c65e7081..841c3fd2d 100644
--- a/launcher/ui/pages/global/AccountListPage.h
+++ b/launcher/ui/pages/global/AccountListPage.h
@@ -62,6 +62,7 @@ public:
public slots:
void on_actionAddMojang_triggered();
void on_actionAddMicrosoft_triggered();
+ void on_actionAddOffline_triggered();
void on_actionRemove_triggered();
void on_actionRefresh_triggered();
void on_actionSetDefault_triggered();
diff --git a/launcher/ui/pages/global/AccountListPage.ui b/launcher/ui/pages/global/AccountListPage.ui
index 29738c023..d21a92e23 100644
--- a/launcher/ui/pages/global/AccountListPage.ui
+++ b/launcher/ui/pages/global/AccountListPage.ui
@@ -54,6 +54,7 @@
+
@@ -103,6 +104,11 @@
Add Microsoft
+
+
+ Add Offline
+
+
Refresh
From a1ff3b1ee34c302ba52d773816207d30badab1eb Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Wed, 12 Jan 2022 14:26:02 +0100
Subject: [PATCH 02/45] Add offline mode support
---
launcher/CMakeLists.txt | 4 +++
launcher/LaunchController.cpp | 6 ++++
launcher/minecraft/auth/AccountData.cpp | 10 +++++-
launcher/minecraft/auth/AccountData.h | 3 +-
launcher/minecraft/auth/AccountList.cpp | 2 +-
launcher/minecraft/auth/MinecraftAccount.cpp | 31 +++++++++++++++++++
launcher/minecraft/auth/MinecraftAccount.h | 12 +++++++
launcher/minecraft/auth/flows/Offline.cpp | 17 ++++++++++
launcher/minecraft/auth/flows/Offline.h | 22 +++++++++++++
launcher/minecraft/auth/steps/OfflineStep.cpp | 18 +++++++++++
launcher/minecraft/auth/steps/OfflineStep.h | 20 ++++++++++++
launcher/ui/dialogs/OfflineLoginDialog.cpp | 4 +--
12 files changed, 144 insertions(+), 5 deletions(-)
create mode 100644 launcher/minecraft/auth/flows/Offline.cpp
create mode 100644 launcher/minecraft/auth/flows/Offline.h
create mode 100644 launcher/minecraft/auth/steps/OfflineStep.cpp
create mode 100644 launcher/minecraft/auth/steps/OfflineStep.h
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index 0052f0e22..df3614475 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -221,7 +221,11 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/Mojang.h
minecraft/auth/flows/MSA.cpp
minecraft/auth/flows/MSA.h
+ minecraft/auth/flows/Offline.cpp
+ minecraft/auth/flows/Offline.h
+ minecraft/auth/steps/OfflineStep.cpp
+ minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/EntitlementsStep.cpp
minecraft/auth/steps/EntitlementsStep.h
minecraft/auth/steps/GetSkinStep.cpp
diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp
index 7750be1a2..32fc99cb9 100644
--- a/launcher/LaunchController.cpp
+++ b/launcher/LaunchController.cpp
@@ -116,6 +116,12 @@ void LaunchController::login() {
m_session->wants_online = m_online;
m_accountToUse->fillSession(m_session);
+ // Launch immediately in true offline mode
+ if(m_accountToUse->isOffline()) {
+ launchInstance();
+ return;
+ }
+
switch(m_accountToUse->accountState()) {
case AccountState::Offline: {
m_session->wants_online = false;
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp
index 7526c9517..9b84fe1a3 100644
--- a/launcher/minecraft/auth/AccountData.cpp
+++ b/launcher/minecraft/auth/AccountData.cpp
@@ -314,6 +314,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
type = AccountType::MSA;
} else if (typeS == "Mojang") {
type = AccountType::Mojang;
+ } else if (typeS == "Offline") {
+ type = AccountType::Offline;
} else {
qWarning() << "Failed to parse account data: type is not recognized.";
return false;
@@ -363,6 +365,9 @@ QJsonObject AccountData::saveState() const {
tokenToJSONV3(output, xboxApiToken, "xrp-main");
tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
}
+ else if (type == AccountType::Offline) {
+ output["type"] = "Offline";
+ }
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
@@ -371,7 +376,7 @@ QJsonObject AccountData::saveState() const {
}
QString AccountData::userName() const {
- if(type != AccountType::Mojang) {
+ if(type == AccountType::MSA) {
return QString();
}
return yggdrasilToken.extra["userName"].toString();
@@ -427,6 +432,9 @@ QString AccountData::accountDisplayString() const {
case AccountType::Mojang: {
return userName();
}
+ case AccountType::Offline: {
+ return userName();
+ }
case AccountType::MSA: {
if(xboxApiToken.extra.contains("gtg")) {
return xboxApiToken.extra["gtg"].toString();
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index abf84e43a..606c1ad11 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -38,7 +38,8 @@ struct MinecraftProfile {
enum class AccountType {
MSA,
- Mojang
+ Mojang,
+ Offline
};
enum class AccountState {
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index ef8b435d1..04470e1c1 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -302,7 +302,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
}
case MigrationColumn: {
- if(account->isMSA()) {
+ if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?");
}
if (account->canMigrate()) {
diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp
index ed9e945ee..6592be0fe 100644
--- a/launcher/minecraft/auth/MinecraftAccount.cpp
+++ b/launcher/minecraft/auth/MinecraftAccount.cpp
@@ -30,6 +30,7 @@
#include "flows/MSA.h"
#include "flows/Mojang.h"
+#include "flows/Offline.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
data.internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
@@ -68,6 +69,23 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA()
return account;
}
+MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
+{
+ MinecraftAccountPtr account = new MinecraftAccount();
+ account->data.type = AccountType::Offline;
+ account->data.yggdrasilToken.token = "offline";
+ account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
+ account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
+ account->data.yggdrasilToken.extra["userName"] = username;
+ account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.minecraftEntitlement.ownsMinecraft = true;
+ account->data.minecraftEntitlement.canPlayMinecraft = true;
+ account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
+ account->data.minecraftProfile.name = username;
+ account->data.minecraftProfile.validity = Katabasis::Validity::Certain;
+ return account;
+}
+
QJsonObject MinecraftAccount::saveToJson() const
{
@@ -111,6 +129,16 @@ shared_qobject_ptr MinecraftAccount::loginMSA() {
return m_currentTask;
}
+shared_qobject_ptr MinecraftAccount::loginOffline() {
+ Q_ASSERT(m_currentTask.get() == nullptr);
+
+ m_currentTask.reset(new OfflineLogin(&data));
+ connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
+ connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
+ emit activityChanged(true);
+ return m_currentTask;
+}
+
shared_qobject_ptr MinecraftAccount::refresh() {
if(m_currentTask) {
return m_currentTask;
@@ -119,6 +147,9 @@ shared_qobject_ptr MinecraftAccount::refresh() {
if(data.type == AccountType::MSA) {
m_currentTask.reset(new MSASilent(&data));
}
+ if(data.type == AccountType::Offline) {
+ m_currentTask.reset(new OfflineRefresh(&data));
+ }
else {
m_currentTask.reset(new MojangRefresh(&data));
}
diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h
index 7ab3c7468..6592f9c07 100644
--- a/launcher/minecraft/auth/MinecraftAccount.h
+++ b/launcher/minecraft/auth/MinecraftAccount.h
@@ -73,6 +73,8 @@ public: /* construction */
static MinecraftAccountPtr createBlankMSA();
+ static MinecraftAccountPtr createOffline(const QString &username);
+
static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json);
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json);
@@ -89,6 +91,8 @@ public: /* manipulation */
shared_qobject_ptr loginMSA();
+ shared_qobject_ptr loginOffline();
+
shared_qobject_ptr refresh();
shared_qobject_ptr currentTask();
@@ -128,6 +132,10 @@ public: /* queries */
return data.type == AccountType::MSA;
}
+ bool isOffline() const {
+ return data.type == AccountType::Offline;
+ }
+
bool ownsMinecraft() const {
return data.minecraftEntitlement.ownsMinecraft;
}
@@ -149,6 +157,10 @@ public: /* queries */
return "msa";
}
break;
+ case AccountType::Offline: {
+ return "offline";
+ }
+ break;
default: {
return "unknown";
}
diff --git a/launcher/minecraft/auth/flows/Offline.cpp b/launcher/minecraft/auth/flows/Offline.cpp
new file mode 100644
index 000000000..fc614a8c7
--- /dev/null
+++ b/launcher/minecraft/auth/flows/Offline.cpp
@@ -0,0 +1,17 @@
+#include "Offline.h"
+
+#include "minecraft/auth/steps/OfflineStep.h"
+
+OfflineRefresh::OfflineRefresh(
+ AccountData *data,
+ QObject *parent
+) : AuthFlow(data, parent) {
+ m_steps.append(new OfflineStep(m_data));
+}
+
+OfflineLogin::OfflineLogin(
+ AccountData *data,
+ QObject *parent
+) : AuthFlow(data, parent) {
+ m_steps.append(new OfflineStep(m_data));
+}
diff --git a/launcher/minecraft/auth/flows/Offline.h b/launcher/minecraft/auth/flows/Offline.h
new file mode 100644
index 000000000..5d1f83a46
--- /dev/null
+++ b/launcher/minecraft/auth/flows/Offline.h
@@ -0,0 +1,22 @@
+#pragma once
+#include "AuthFlow.h"
+
+class OfflineRefresh : public AuthFlow
+{
+ Q_OBJECT
+public:
+ explicit OfflineRefresh(
+ AccountData *data,
+ QObject *parent = 0
+ );
+};
+
+class OfflineLogin : public AuthFlow
+{
+ Q_OBJECT
+public:
+ explicit OfflineLogin(
+ AccountData *data,
+ QObject *parent = 0
+ );
+};
diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp
new file mode 100644
index 000000000..9f1fc2666
--- /dev/null
+++ b/launcher/minecraft/auth/steps/OfflineStep.cpp
@@ -0,0 +1,18 @@
+#include "OfflineStep.h"
+
+#include "Application.h"
+
+OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {};
+OfflineStep::~OfflineStep() noexcept = default;
+
+QString OfflineStep::describe() {
+ return tr("Creating offline account.");
+}
+
+void OfflineStep::rehydrate() {
+ // NOOP
+}
+
+void OfflineStep::perform() {
+ emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account."));
+}
diff --git a/launcher/minecraft/auth/steps/OfflineStep.h b/launcher/minecraft/auth/steps/OfflineStep.h
new file mode 100644
index 000000000..62addb1f7
--- /dev/null
+++ b/launcher/minecraft/auth/steps/OfflineStep.h
@@ -0,0 +1,20 @@
+
+#pragma once
+#include
+
+#include "QObjectPtr.h"
+#include "minecraft/auth/AuthStep.h"
+
+#include
+
+class OfflineStep : public AuthStep {
+ Q_OBJECT
+public:
+ explicit OfflineStep(AccountData *data);
+ virtual ~OfflineStep() noexcept;
+
+ void perform() override;
+ void rehydrate() override;
+
+ QString describe() override;
+};
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp
index f6ecc4e97..0cc922f83 100644
--- a/launcher/ui/dialogs/OfflineLoginDialog.cpp
+++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp
@@ -42,8 +42,8 @@ void OfflineLoginDialog::accept()
ui->progressBar->setVisible(true);
// Setup the login task and start it
- m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
- m_loginTask = m_account->login("TODO: create offline mode account flow");
+ m_account = MinecraftAccount::createOffline(ui->userTextBox->text());
+ m_loginTask = m_account->loginOffline();
connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus);
From 6ecc8c5496cd1fa121b69f770c0664320fd7dc1d Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Wed, 12 Jan 2022 14:57:32 +0100
Subject: [PATCH 03/45] Remove unnecessary license header
---
launcher/ui/dialogs/OfflineLoginDialog.cpp | 15 ---------------
launcher/ui/dialogs/OfflineLoginDialog.h | 15 ---------------
2 files changed, 30 deletions(-)
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.cpp b/launcher/ui/dialogs/OfflineLoginDialog.cpp
index 0cc922f83..345ed40ac 100644
--- a/launcher/ui/dialogs/OfflineLoginDialog.cpp
+++ b/launcher/ui/dialogs/OfflineLoginDialog.cpp
@@ -1,18 +1,3 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
#include "OfflineLoginDialog.h"
#include "ui_OfflineLoginDialog.h"
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.h b/launcher/ui/dialogs/OfflineLoginDialog.h
index ba0c18232..5e6083792 100644
--- a/launcher/ui/dialogs/OfflineLoginDialog.h
+++ b/launcher/ui/dialogs/OfflineLoginDialog.h
@@ -1,18 +1,3 @@
-/* Copyright 2013-2021 MultiMC Contributors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
#pragma once
#include
From 46a3b4de6ebb625b958a69aba85316171d3fa168 Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Wed, 12 Jan 2022 18:41:33 +0100
Subject: [PATCH 04/45] Remove unnecessary semicolon
---
launcher/minecraft/auth/steps/OfflineStep.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/launcher/minecraft/auth/steps/OfflineStep.cpp b/launcher/minecraft/auth/steps/OfflineStep.cpp
index 9f1fc2666..dc092bfd4 100644
--- a/launcher/minecraft/auth/steps/OfflineStep.cpp
+++ b/launcher/minecraft/auth/steps/OfflineStep.cpp
@@ -2,7 +2,7 @@
#include "Application.h"
-OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {};
+OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {}
OfflineStep::~OfflineStep() noexcept = default;
QString OfflineStep::describe() {
From 395e2655648dbb80d089077e6a6b2530f4876c63 Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Fri, 14 Jan 2022 00:01:05 +0100
Subject: [PATCH 05/45] Add offline mode disclaimer
---
launcher/ui/dialogs/OfflineLoginDialog.ui | 4 ++--
launcher/ui/pages/global/AccountListPage.cpp | 8 +++++++-
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/launcher/ui/dialogs/OfflineLoginDialog.ui b/launcher/ui/dialogs/OfflineLoginDialog.ui
index d8964a2e4..4577d361b 100644
--- a/launcher/ui/dialogs/OfflineLoginDialog.ui
+++ b/launcher/ui/dialogs/OfflineLoginDialog.ui
@@ -6,8 +6,8 @@
0
0
- 400
- 150
+ 500
+ 250
diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp
index b9aa7628b..1c27d5b73 100644
--- a/launcher/ui/pages/global/AccountListPage.cpp
+++ b/launcher/ui/pages/global/AccountListPage.cpp
@@ -158,7 +158,13 @@ void AccountListPage::on_actionAddOffline_triggered()
{
MinecraftAccountPtr account = OfflineLoginDialog::newAccount(
this,
- tr("Please enter your desired username to add your offline account.")
+ tr("Please enter your desired username to add your offline account.
"
+ "
"
+ "It is required by Mojang that you own Minecraft BEFORE you may use offline mode.
"
+ "The PolyMC organization denounces piracy and takes NO LIABILITY WHATSOEVER
"
+ "for any illegal activity that may occur in usage of the offline mode feature.
"
+ "
"
+ "By continuing you promise that you own a Minecraft account.")
);
if (account)
From cdaa397dcffb92ae6a9c659047a87d49286dee4f Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Fri, 14 Jan 2022 14:19:31 +0100
Subject: [PATCH 06/45] Reword offline mode disclaimer
---
launcher/ui/pages/global/AccountListPage.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/launcher/ui/pages/global/AccountListPage.cpp b/launcher/ui/pages/global/AccountListPage.cpp
index 1c27d5b73..ad88812ae 100644
--- a/launcher/ui/pages/global/AccountListPage.cpp
+++ b/launcher/ui/pages/global/AccountListPage.cpp
@@ -161,8 +161,8 @@ void AccountListPage::on_actionAddOffline_triggered()
tr("Please enter your desired username to add your offline account.
"
"
"
"It is required by Mojang that you own Minecraft BEFORE you may use offline mode.
"
- "The PolyMC organization denounces piracy and takes NO LIABILITY WHATSOEVER
"
- "for any illegal activity that may occur in usage of the offline mode feature.
"
+ "The PolyMC developers denounce piracy and take NO LIABILITY WHATSOEVER for
"
+ "any illegal activity that may occur in usage of the offline mode feature.
"
"
"
"By continuing you promise that you own a Minecraft account.")
);
From 41dba376a803825fca2dc6b3b812ff8a15a9588e Mon Sep 17 00:00:00 2001
From: swirl
Date: Fri, 14 Jan 2022 17:33:34 -0500
Subject: [PATCH 07/45] remove 5 from display name
Closes: #58
---
program_info/CMakeLists.txt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/program_info/CMakeLists.txt b/program_info/CMakeLists.txt
index d2f23277f..77b971fcd 100644
--- a/program_info/CMakeLists.txt
+++ b/program_info/CMakeLists.txt
@@ -3,8 +3,8 @@ set(Launcher_CommonName "PolyMC")
set(Launcher_Copyright "PolyMC Contributors" PARENT_SCOPE)
set(Launcher_Domain "github.com/PolyMC" PARENT_SCOPE)
set(Launcher_Name "${Launcher_CommonName}" PARENT_SCOPE)
-set(Launcher_DisplayName "${Launcher_CommonName} 5" PARENT_SCOPE)
-set(Launcher_UserAgent "${Launcher_CommonName}/5.0" PARENT_SCOPE)
+set(Launcher_DisplayName "${Launcher_CommonName}" PARENT_SCOPE)
+set(Launcher_UserAgent "${Launcher_CommonName}/${Launcher_RELEASE_VERSION_NAME}" PARENT_SCOPE)
set(Launcher_ConfigFile "polymc.cfg" PARENT_SCOPE)
set(Launcher_Git "https://github.com/PolyMC/PolyMC" PARENT_SCOPE)
From b19e3156154ba0dd232a3d165b1759c57e2858f2 Mon Sep 17 00:00:00 2001
From: swirl
Date: Fri, 14 Jan 2022 17:34:50 -0500
Subject: [PATCH 08/45] Set maximum memory allocated to 4GB by default
---
launcher/Application.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index a3e2c44f9..47c9c20e5 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -662,7 +662,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
// Memory
m_settings->registerSetting({"MinMemAlloc", "MinMemoryAlloc"}, 512);
- m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 1024);
+ m_settings->registerSetting({"MaxMemAlloc", "MaxMemoryAlloc"}, 4096);
m_settings->registerSetting("PermGen", 128);
// Java Settings
From a62155c1c9e561327cc589fe3da7b6d5a107d58d Mon Sep 17 00:00:00 2001
From: swirl
Date: Fri, 14 Jan 2022 18:20:06 -0500
Subject: [PATCH 09/45] preliminary stuff for paste.ee removal
---
CMakeLists.txt | 3 -
buildconfig/BuildConfig.cpp.in | 1 -
buildconfig/BuildConfig.h | 5 --
launcher/Application.cpp | 4 +-
launcher/CMakeLists.txt | 6 +-
.../global/{PasteEEPage.cpp => PastePage.cpp} | 22 ++++----
.../global/{PasteEEPage.h => PastePage.h} | 11 ++--
.../global/{PasteEEPage.ui => PastePage.ui} | 55 ++++++-------------
8 files changed, 40 insertions(+), 67 deletions(-)
rename launcher/ui/pages/global/{PasteEEPage.cpp => PastePage.cpp} (81%)
rename launcher/ui/pages/global/{PasteEEPage.h => PastePage.h} (88%)
rename launcher/ui/pages/global/{PasteEEPage.ui => PastePage.ui} (61%)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2af0aa712..91119b2f0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -68,9 +68,6 @@ set(Launcher_NOTIFICATION_URL "" CACHE STRING "URL for checking for notification
# The metadata server
set(Launcher_META_URL "https://meta.multimc.org/v1/" CACHE STRING "URL to fetch Launcher's meta files from.")
-# paste.ee API key
-set(Launcher_PASTE_EE_API_KEY "utLvciUouSURFzfjPxLBf5W4ISsUX4pwBDF7N1AfZ" CACHE STRING "API key you can get from paste.ee when you register an account")
-
# Imgur API Client ID
set(Launcher_IMGUR_CLIENT_ID "5b97b0713fba4a3" CACHE STRING "Client ID you can get from Imgur when you register an application")
diff --git a/buildconfig/BuildConfig.cpp.in b/buildconfig/BuildConfig.cpp.in
index af8845dce..2595f78b2 100644
--- a/buildconfig/BuildConfig.cpp.in
+++ b/buildconfig/BuildConfig.cpp.in
@@ -42,7 +42,6 @@ Config::Config()
VERSION_STR = "@Launcher_VERSION_STRING@";
NEWS_RSS_URL = "@Launcher_NEWS_RSS_URL@";
- PASTE_EE_KEY = "@Launcher_PASTE_EE_API_KEY@";
IMGUR_CLIENT_ID = "@Launcher_IMGUR_CLIENT_ID@";
MSA_CLIENT_ID = "@Launcher_MSA_CLIENT_ID@";
META_URL = "@Launcher_META_URL@";
diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h
index 009fb2bca..d09d5288f 100644
--- a/buildconfig/BuildConfig.h
+++ b/buildconfig/BuildConfig.h
@@ -67,11 +67,6 @@ public:
*/
QString NEWS_RSS_URL;
- /**
- * API key you can get from paste.ee when you register an account
- */
- QString PASTE_EE_KEY;
-
/**
* Client ID you can get from Imgur when you register an application
*/
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 47c9c20e5..98e3e0fc0 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -14,7 +14,7 @@
#include "ui/pages/global/ProxyPage.h"
#include "ui/pages/global/ExternalToolsPage.h"
#include "ui/pages/global/AccountListPage.h"
-#include "ui/pages/global/PasteEEPage.h"
+#include "ui/pages/global/PastePage.h"
#include "ui/pages/global/CustomCommandsPage.h"
#include "ui/themes/ITheme.h"
@@ -728,7 +728,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_globalSettingsProvider->addPage();
m_globalSettingsProvider->addPage();
m_globalSettingsProvider->addPage();
- m_globalSettingsProvider->addPage();
+ m_globalSettingsProvider->addPage();
}
qDebug() << "<> Settings loaded.";
}
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index df3614475..21859cad2 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -711,8 +711,8 @@ SET(LAUNCHER_SOURCES
ui/pages/global/LauncherPage.h
ui/pages/global/ProxyPage.cpp
ui/pages/global/ProxyPage.h
- ui/pages/global/PasteEEPage.cpp
- ui/pages/global/PasteEEPage.h
+ ui/pages/global/PastePage.cpp
+ ui/pages/global/PastePage.h
# GUI - platform pages
ui/pages/modplatform/VanillaPage.cpp
@@ -848,7 +848,7 @@ qt5_wrap_ui(LAUNCHER_UI
ui/pages/global/AccountListPage.ui
ui/pages/global/JavaPage.ui
ui/pages/global/LauncherPage.ui
- ui/pages/global/PasteEEPage.ui
+ ui/pages/global/PastePage.ui
ui/pages/global/ProxyPage.ui
ui/pages/global/MinecraftPage.ui
ui/pages/global/ExternalToolsPage.ui
diff --git a/launcher/ui/pages/global/PasteEEPage.cpp b/launcher/ui/pages/global/PastePage.cpp
similarity index 81%
rename from launcher/ui/pages/global/PasteEEPage.cpp
rename to launcher/ui/pages/global/PastePage.cpp
index 4b375d9a2..3378a6efc 100644
--- a/launcher/ui/pages/global/PasteEEPage.cpp
+++ b/launcher/ui/pages/global/PastePage.cpp
@@ -1,4 +1,4 @@
-/* Copyright 2013-2021 MultiMC Contributors
+/* Copyright 2013-2021 MultiMC & PolyMC Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,8 +13,8 @@
* limitations under the License.
*/
-#include "PasteEEPage.h"
-#include "ui_PasteEEPage.h"
+#include "PastePage.h"
+#include "ui_PastePage.h"
#include
#include
@@ -25,22 +25,22 @@
#include "tools/BaseProfiler.h"
#include "Application.h"
-PasteEEPage::PasteEEPage(QWidget *parent) :
+PastePage::PastePage(QWidget *parent) :
QWidget(parent),
- ui(new Ui::PasteEEPage)
+ ui(new Ui::PastePage)
{
ui->setupUi(this);
ui->tabWidget->tabBar()->hide();\
- connect(ui->customAPIkeyEdit, &QLineEdit::textEdited, this, &PasteEEPage::textEdited);
+ connect(ui->customAPIkeyEdit, &QLineEdit::textEdited, this, &PastePage::textEdited);
loadSettings();
}
-PasteEEPage::~PasteEEPage()
+PastePage::~PastePage()
{
delete ui;
}
-void PasteEEPage::loadSettings()
+void PastePage::loadSettings()
{
auto s = APPLICATION->settings();
QString keyToUse = s->get("PasteEEAPIKey").toString();
@@ -55,7 +55,7 @@ void PasteEEPage::loadSettings()
}
}
-void PasteEEPage::applySettings()
+void PastePage::applySettings()
{
auto s = APPLICATION->settings();
@@ -69,13 +69,13 @@ void PasteEEPage::applySettings()
s->set("PasteEEAPIKey", pasteKeyToUse);
}
-bool PasteEEPage::apply()
+bool PastePage::apply()
{
applySettings();
return true;
}
-void PasteEEPage::textEdited(const QString& text)
+void PastePage::textEdited(const QString& text)
{
ui->customButton->setChecked(true);
}
diff --git a/launcher/ui/pages/global/PasteEEPage.h b/launcher/ui/pages/global/PastePage.h
similarity index 88%
rename from launcher/ui/pages/global/PasteEEPage.h
rename to launcher/ui/pages/global/PastePage.h
index a1c7d434b..3930d4ec1 100644
--- a/launcher/ui/pages/global/PasteEEPage.h
+++ b/launcher/ui/pages/global/PastePage.h
@@ -21,16 +21,16 @@
#include
namespace Ui {
-class PasteEEPage;
+class PastePage;
}
-class PasteEEPage : public QWidget, public BasePage
+class PastePage : public QWidget, public BasePage
{
Q_OBJECT
public:
- explicit PasteEEPage(QWidget *parent = 0);
- ~PasteEEPage();
+ explicit PastePage(QWidget *parent = 0);
+ ~PastePage();
QString displayName() const override
{
@@ -58,5 +58,6 @@ private slots:
void textEdited(const QString &text);
private:
- Ui::PasteEEPage *ui;
+ Ui::PastePage *ui;
};
+
diff --git a/launcher/ui/pages/global/PasteEEPage.ui b/launcher/ui/pages/global/PastePage.ui
similarity index 61%
rename from launcher/ui/pages/global/PasteEEPage.ui
rename to launcher/ui/pages/global/PastePage.ui
index 108837817..0bef5a226 100644
--- a/launcher/ui/pages/global/PasteEEPage.ui
+++ b/launcher/ui/pages/global/PastePage.ui
@@ -1,7 +1,7 @@
- PasteEEPage
-
+ PastePage
+
0
@@ -36,39 +36,9 @@
-
- paste.ee API key
+ Pastebin Site
-
-
-
-
- MultiMC key - 12MB &upload limit
-
-
- pasteButtonGroup
-
-
-
- -
-
-
- &Your own key - 12MB upload limit:
-
-
- pasteButtonGroup
-
-
-
- -
-
-
- QLineEdit::Password
-
-
- Paste your API key here!
-
-
-
-
@@ -76,10 +46,24 @@
+ -
+
+
-
+
+ 0x0.st
+
+
+ -
+
+ paste.polymc.org
+
+
+
+
-
- <html><head/><body><p><a href="https://paste.ee">paste.ee</a> is used by MultiMC for log uploads. If you have a <a href="https://paste.ee">paste.ee</a> account, you can add your API key here and have your uploaded logs paired with your account.</p></body></html>
+ <html><head/><body><p>paste.polymc.org is a pastebin managed by PolyMC's lead maintainer. Something something trust</p></body></html>
Qt::RichText
@@ -116,9 +100,6 @@
tabWidget
- multimcButton
- customButton
- customAPIkeyEdit
From a606b47a22443cefc52d865df24c45ff50908f6f Mon Sep 17 00:00:00 2001
From: swirl
Date: Fri, 14 Jan 2022 18:30:02 -0500
Subject: [PATCH 10/45] pastebin URL app setting
---
launcher/Application.cpp | 4 ++--
launcher/ui/pages/global/PastePage.cpp | 13 +++----------
launcher/ui/pages/global/PastePage.ui | 2 +-
3 files changed, 6 insertions(+), 13 deletions(-)
diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 98e3e0fc0..110b2e6b5 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -714,8 +714,8 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", "");
- // paste.ee API key
- m_settings->registerSetting("PasteEEAPIKey", "multimc");
+ // pastebin URL
+ m_settings->registerSetting("PastebinURL", "0x0.st");
// Init page provider
{
diff --git a/launcher/ui/pages/global/PastePage.cpp b/launcher/ui/pages/global/PastePage.cpp
index 3378a6efc..495e9937f 100644
--- a/launcher/ui/pages/global/PastePage.cpp
+++ b/launcher/ui/pages/global/PastePage.cpp
@@ -43,16 +43,9 @@ PastePage::~PastePage()
void PastePage::loadSettings()
{
auto s = APPLICATION->settings();
- QString keyToUse = s->get("PasteEEAPIKey").toString();
- if(keyToUse == "multimc")
- {
- ui->multimcButton->setChecked(true);
- }
- else
- {
- ui->customButton->setChecked(true);
- ui->customAPIkeyEdit->setText(keyToUse);
- }
+ QString pastebin = s->get("PastebinURL");
+ int index = ui->urlChoices->findText(pastebin);
+ ui->urlChoices->setCurrentIndex(index);
}
void PastePage::applySettings()
diff --git a/launcher/ui/pages/global/PastePage.ui b/launcher/ui/pages/global/PastePage.ui
index 0bef5a226..784ea3f42 100644
--- a/launcher/ui/pages/global/PastePage.ui
+++ b/launcher/ui/pages/global/PastePage.ui
@@ -47,7 +47,7 @@
-
-
+
-
0x0.st
From 6cd4375aff98554c56a0138208d869aca0587656 Mon Sep 17 00:00:00 2001
From: swirl
Date: Fri, 14 Jan 2022 18:39:25 -0500
Subject: [PATCH 11/45] add arch linux package to readme
closes: #53
---
README.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/README.md b/README.md
index d9d414e73..7b67c5882 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,7 @@ Several source build packages are available, along with experimental pre-built g
- [Windows (32-bit)](https://packages.polymc.org/latest/win32/win32.zip) ([SHA256](https://packages.polymc.org/latest/win32/win32.zip.sha256)) - this is a portable package, you can extract it anywhere and run it. This package needs testing.
- [Debian (AMD64)](https://packages.polymc.org/latest/deb/polymc-amd64.deb) ([SHA256](https://packages.polymc.org/latest/deb/polymc-amd64.deb.sha256)) - this is intended to be installed with `dpkg -i`. Alternatively, you may build the `.deb` yourself, by going to `packages/debian` and running `./makedeb.sh`.
- [AppImage (AMD64)](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage) ([SHA256](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage.sha256)) - `chmod +x` must be run on this file before usage. This should work on any distribution.
+- [Arch Linux (AMD64)](https://packages.polymc.org/latest/arch/polymc-bin-latest-1-x86_64.pkg.tar.zst) ([SHA256](https://packages.polymc.org/latest/arch/polymc-bin-latest-1-x86_64.pkg.tar.zst.sha256) - this is intended to be installed with `pacman -U`. This is an alternative if building the AUR package is not desired.
- MacOS currently does not have any packages. We are still working on setting up MacOS packaging.
## Development
From ac93c64cd40be038a0f3a71df18686c5e1f955c3 Mon Sep 17 00:00:00 2001
From: Thomas Sirack
Date: Fri, 14 Jan 2022 16:59:16 -0700
Subject: [PATCH 12/45] Fix program executable name for shell script
---
CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2af0aa712..a19556cbd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -124,7 +124,7 @@ endif()
####################################### Program Info #######################################
-set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
+set(Launcher_APP_BINARY_NAME "PolyMC" CACHE STRING "Name of the Launcher binary")
add_subdirectory(program_info)
####################################### Install layout #######################################
From 0bbd0ac0b9b0ba7212ed15d4628577bf92005a18 Mon Sep 17 00:00:00 2001
From: Thomas Sirack
Date: Fri, 14 Jan 2022 19:28:10 -0700
Subject: [PATCH 13/45] Change method of shell script fix per suggestion
The Launcher.in file is now modified rather than CMakeLists.txt
---
CMakeLists.txt | 2 +-
launcher/Launcher.in | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a19556cbd..2af0aa712 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -124,7 +124,7 @@ endif()
####################################### Program Info #######################################
-set(Launcher_APP_BINARY_NAME "PolyMC" CACHE STRING "Name of the Launcher binary")
+set(Launcher_APP_BINARY_NAME "polymc" CACHE STRING "Name of the Launcher binary")
add_subdirectory(program_info)
####################################### Install layout #######################################
diff --git a/launcher/Launcher.in b/launcher/Launcher.in
index b79b276bc..5e5e2c2bc 100755
--- a/launcher/Launcher.in
+++ b/launcher/Launcher.in
@@ -14,7 +14,7 @@ if [[ $EUID -eq 0 ]]; then
fi
-LAUNCHER_NAME=@Launcher_Name@
+LAUNCHER_NAME=@Launcher_APP_BINARY_NAME@
LAUNCHER_DIR="$(dirname "$(readlink -f "$0")")"
echo "Launcher Dir: ${LAUNCHER_DIR}"
From dc129fd8868f1888fcc55136f3cdc2486ae8ab6a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Mr=C3=A1zek?=
Date: Sat, 8 Jan 2022 11:14:07 +0100
Subject: [PATCH 14/45] GH-4125 workaround for java printing garbage to stdout
on bedrock linux
---
launcher/java/JavaChecker.cpp | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp
index 80c599cc1..c3132af35 100644
--- a/launcher/java/JavaChecker.cpp
+++ b/launcher/java/JavaChecker.cpp
@@ -61,6 +61,10 @@ void JavaChecker::stdoutReady()
QByteArray data = process->readAllStandardOutput();
QString added = QString::fromLocal8Bit(data);
added.remove('\r');
+ // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
+ if (added.contains("/bedrock/strata")) {
+ return;
+ }
m_stdout += added;
}
From f78bb90ed9f2658eee9259a472efe47a25eddc1e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Mr=C3=A1zek?=
Date: Sat, 8 Jan 2022 12:26:16 +0100
Subject: [PATCH 15/45] GH-4125 fix it better
---
launcher/java/JavaChecker.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/launcher/java/JavaChecker.cpp b/launcher/java/JavaChecker.cpp
index c3132af35..4557784b0 100644
--- a/launcher/java/JavaChecker.cpp
+++ b/launcher/java/JavaChecker.cpp
@@ -61,10 +61,6 @@ void JavaChecker::stdoutReady()
QByteArray data = process->readAllStandardOutput();
QString added = QString::fromLocal8Bit(data);
added.remove('\r');
- // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
- if (added.contains("/bedrock/strata")) {
- return;
- }
m_stdout += added;
}
@@ -107,6 +103,10 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status)
for(QString line : lines)
{
line = line.trimmed();
+ // NOTE: workaround for GH-4125, where garbage is getting printed into stdout on bedrock linux
+ if (line.contains("/bedrock/strata")) {
+ continue;
+ }
auto parts = line.split('=', QString::SkipEmptyParts);
if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty())
From b95c27ceef1a1742d62ae0a758657fd0beea18c6 Mon Sep 17 00:00:00 2001
From: swirl
Date: Sat, 15 Jan 2022 21:25:49 -0500
Subject: [PATCH 16/45] fix readme formatting
---
README.md | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 7b67c5882..d97ab67c0 100644
--- a/README.md
+++ b/README.md
@@ -3,6 +3,7 @@
+
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
This is a **fork** of the MultiMC Launcher and not endorsed by MultiMC. The PolyMC community felt that the maintainer was not acting in the spirit of Free Software so this fork was made. Read "[Why was this fork made?](https://github.com/PolyMC/PolyMC/wiki/FAQ)" on the wiki for more details.
@@ -23,7 +24,7 @@ Several source build packages are available, along with experimental pre-built g
- [Windows (32-bit)](https://packages.polymc.org/latest/win32/win32.zip) ([SHA256](https://packages.polymc.org/latest/win32/win32.zip.sha256)) - this is a portable package, you can extract it anywhere and run it. This package needs testing.
- [Debian (AMD64)](https://packages.polymc.org/latest/deb/polymc-amd64.deb) ([SHA256](https://packages.polymc.org/latest/deb/polymc-amd64.deb.sha256)) - this is intended to be installed with `dpkg -i`. Alternatively, you may build the `.deb` yourself, by going to `packages/debian` and running `./makedeb.sh`.
- [AppImage (AMD64)](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage) ([SHA256](https://packages.polymc.org/latest/appimage/PolyMC-latest-x86_64.AppImage.sha256)) - `chmod +x` must be run on this file before usage. This should work on any distribution.
-- [Arch Linux (AMD64)](https://packages.polymc.org/latest/arch/polymc-bin-latest-1-x86_64.pkg.tar.zst) ([SHA256](https://packages.polymc.org/latest/arch/polymc-bin-latest-1-x86_64.pkg.tar.zst.sha256) - this is intended to be installed with `pacman -U`. This is an alternative if building the AUR package is not desired.
+- [Arch Linux (AMD64)](https://packages.polymc.org/latest/arch/polymc-bin-latest-1-x86_64.pkg.tar.zst) ([SHA256](https://packages.polymc.org/latest/arch/polymc-bin-latest-1-x86_64.pkg.tar.zst.sha256)) - this is intended to be installed with `pacman -U`. This is an alternative if building the AUR package is not desired.
- MacOS currently does not have any packages. We are still working on setting up MacOS packaging.
## Development
From 8172dcd2d53114df05a23fa58e3832d2011f2e17 Mon Sep 17 00:00:00 2001
From: bexnoss <82064510+bexnoss@users.noreply.github.com>
Date: Sun, 16 Jan 2022 07:55:10 +0100
Subject: [PATCH 17/45] Replace PNG README header with SVG
Instead of GitHub specific MarkDown based theming this uses CSS in SVG.
---
README.md | 3 +--
program_info/polymc-dark.png | Bin 50722 -> 0 bytes
program_info/polymc-header.svg | 38 +++++++++++++++++++++++++++++++++
program_info/polymc-light.png | Bin 48991 -> 0 bytes
4 files changed, 39 insertions(+), 2 deletions(-)
delete mode 100644 program_info/polymc-dark.png
create mode 100644 program_info/polymc-header.svg
delete mode 100644 program_info/polymc-light.png
diff --git a/README.md b/README.md
index d9d414e73..40d4c6356 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
-
-
+
PolyMC is a custom launcher for Minecraft that focuses on predictability, long term stability and simplicity.
diff --git a/program_info/polymc-dark.png b/program_info/polymc-dark.png
deleted file mode 100644
index cedf6cef79a83483da55ec682ca6081e860f67e0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 50722
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