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 b5c52afa..0052f0e2 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 00000000..f6ecc4e9
--- /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 00000000..ba0c1823
--- /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 00000000..d8964a2e
--- /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 b8da6c75..b9aa7628 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 1c65e708..841c3fd2 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 29738c02..d21a92e2 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 0052f0e2..df361447 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 7750be1a..32fc99cb 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 7526c951..9b84fe1a 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 abf84e43..606c1ad1 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 ef8b435d..04470e1c 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 ed9e945e..6592be0f 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 7ab3c746..6592f9c0 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 00000000..fc614a8c
--- /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 00000000..5d1f83a4
--- /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 00000000..9f1fc266
--- /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 00000000..62addb1f
--- /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 f6ecc4e9..0cc922f8 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 0cc922f8..345ed40a 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 ba0c1823..5e608379 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 9f1fc266..dc092bfd 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 d8964a2e..4577d361 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 b9aa7628..1c27d5b7 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 1c27d5b7..ad88812a 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 d2f23277..77b971fc 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 a3e2c44f..47c9c20e 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 2af0aa71..91119b2f 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 af8845dc..2595f78b 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 009fb2bc..d09d5288 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 47c9c20e..98e3e0fc 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 df361447..21859cad 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 4b375d9a..3378a6ef 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 a1c7d434..3930d4ec 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 10883781..0bef5a22 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 98e3e0fc..110b2e6b 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 3378a6ef..495e9937 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 0bef5a22..784ea3f4 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 d9d414e7..7b67c588 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 2af0aa71..a19556cb 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 a19556cb..2af0aa71 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 b79b276b..5e5e2c2b 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 80c599cc..c3132af3 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 c3132af3..4557784b 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 7b67c588..d97ab67c 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 d9d414e7..40d4c635 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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