diff --git a/launcher/Application.cpp b/launcher/Application.cpp
index 00005c93..1292f8ee 100644
--- a/launcher/Application.cpp
+++ b/launcher/Application.cpp
@@ -773,6 +773,9 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_settings->registerSetting("UpdateDialogGeometry", "");
+ m_settings->registerSetting("AccountsDialogGeometry", "");
+ m_settings->registerSetting("AccountsDialogSplitterState", "");
+
// paste.ee API key
m_settings->registerSetting("PasteEEAPIKey", "multimc");
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp
index 54086ad7..c25c4e2f 100644
--- a/launcher/minecraft/auth/AccountData.cpp
+++ b/launcher/minecraft/auth/AccountData.cpp
@@ -245,19 +245,15 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
return false;
}
auto typeS = typeV.toString();
- if(typeS == "MSA") {
- type = AccountType::MSA;
- } else {
+ if(typeS != "MSA") {
qWarning() << "Failed to parse account data: type is not recognized.";
return false;
}
- if(type == AccountType::MSA) {
- msaToken = tokenFromJSONV3(data, "msa");
- userToken = tokenFromJSONV3(data, "utoken");
- xboxApiToken = tokenFromJSONV3(data, "xrp-main");
- mojangservicesToken = tokenFromJSONV3(data, "xrp-mc");
- }
+ msaToken = tokenFromJSONV3(data, "msa");
+ userToken = tokenFromJSONV3(data, "utoken");
+ xboxApiToken = tokenFromJSONV3(data, "xrp-main");
+ mojangservicesToken = tokenFromJSONV3(data, "xrp-mc");
yggdrasilToken = tokenFromJSONV3(data, "ygg");
minecraftProfile = profileFromJSONV3(data, "profile");
@@ -275,13 +271,11 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
QJsonObject AccountData::saveState() const {
QJsonObject output;
- if (type == AccountType::MSA) {
- output["type"] = "MSA";
- tokenToJSONV3(output, msaToken, "msa");
- tokenToJSONV3(output, userToken, "utoken");
- tokenToJSONV3(output, xboxApiToken, "xrp-main");
- tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
- }
+ output["type"] = "MSA";
+ tokenToJSONV3(output, msaToken, "msa");
+ tokenToJSONV3(output, userToken, "utoken");
+ tokenToJSONV3(output, xboxApiToken, "xrp-main");
+ tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index 6926eaad..4160b181 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -36,10 +36,6 @@ struct MinecraftProfile {
Katabasis::Validity validity = Katabasis::Validity::None;
};
-enum class AccountType {
- MSA
-};
-
enum class AccountState {
Unchecked,
Offline,
@@ -68,11 +64,6 @@ struct AccountData {
QString lastError() const;
- AccountType type = AccountType::MSA;
- bool legacy = false;
- bool canMigrateToMSA = false;
- bool mustMigrateToMSA = false;
-
Katabasis::Token msaToken;
Katabasis::Token userToken;
Katabasis::Token xboxApiToken;
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index 2ad59996..7df64e3d 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -451,6 +451,8 @@ bool AccountList::loadList()
return false;
}
+ m_accounts.append(Entry{false, nullptr});
+
QFile file(m_listFilePath);
// Try to open the file and fail if we can't.
@@ -505,7 +507,6 @@ bool AccountList::loadList()
bool AccountList::loadV3(QJsonObject& root) {
beginResetModel();
- m_accounts.append(Entry{false, nullptr});
QJsonArray accounts = root.value("accounts").toArray();
for (QJsonValue accountVal : accounts)
{
diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp
index 2a461517..826c689d 100644
--- a/launcher/minecraft/auth/MinecraftAccount.cpp
+++ b/launcher/minecraft/auth/MinecraftAccount.cpp
@@ -48,7 +48,6 @@ MinecraftAccountPtr MinecraftAccount::loadFromJsonV3(const QJsonObject& json) {
MinecraftAccountPtr MinecraftAccount::createBlankMSA()
{
MinecraftAccountPtr account(new MinecraftAccount());
- account->data.type = AccountType::MSA;
return account;
}
diff --git a/launcher/minecraft/auth/MinecraftAccount.h b/launcher/minecraft/auth/MinecraftAccount.h
index 464d1aa2..3cb96dea 100644
--- a/launcher/minecraft/auth/MinecraftAccount.h
+++ b/launcher/minecraft/auth/MinecraftAccount.h
@@ -116,10 +116,6 @@ public: /* queries */
bool isActive() const;
- bool canMigrate() const {
- return data.canMigrateToMSA;
- }
-
bool ownsMinecraft() const {
return data.minecraftEntitlement.ownsMinecraft;
}
@@ -129,15 +125,7 @@ public: /* queries */
}
QString typeString() const {
- switch(data.type) {
- case AccountType::MSA: {
- return "msa";
- }
- break;
- default: {
- return "unknown";
- }
- }
+ return "msa";
}
QPixmap getFace() const;
diff --git a/launcher/ui/dialogs/AccountsDialog.cpp b/launcher/ui/dialogs/AccountsDialog.cpp
index b6e2e6e9..a972a54a 100644
--- a/launcher/ui/dialogs/AccountsDialog.cpp
+++ b/launcher/ui/dialogs/AccountsDialog.cpp
@@ -59,10 +59,19 @@ AccountsDialog::AccountsDialog(QWidget *parent, const QString& internalId) : QDi
connect(ui->saveSkinButton, &QPushButton::clicked, this, &AccountsDialog::onSaveSkinClicked);
connect(ui->openSkinsButton, &QPushButton::clicked, this, &AccountsDialog::onOpenSkinsFolderClicked);
- connect(ui->refreshButton, &QPushButton::clicked, this, &AccountsDialog::onRefreshButtonClicked);
connect(ui->signOutButton, &QPushButton::clicked, this, &AccountsDialog::onSignOutButtonClicked);
- connect(ui->refreshButton_Setup, &QPushButton::clicked, this, &AccountsDialog::onRefreshButtonClicked);
connect(ui->signOutButton_Setup, &QPushButton::clicked, this, &AccountsDialog::onSignOutButtonClicked);
+ connect(ui->signOutButton_Demo, &QPushButton::clicked, this, &AccountsDialog::onSignOutButtonClicked);
+ connect(ui->signOutButton_Expired, &QPushButton::clicked, this, &AccountsDialog::onSignOutButtonClicked);
+
+ connect(ui->removeAndSignInButton, &QPushButton::clicked, this, &AccountsDialog::onRemoveAndSignInButtonClicked);
+
+ connect(ui->getMinecraftButton, &QPushButton::clicked, this, &AccountsDialog::onGetMinecraftButtonClicked);
+
+ connect(ui->refreshButton, &QPushButton::clicked, this, &AccountsDialog::onRefreshButtonClicked);
+ connect(ui->refreshButton_Setup, &QPushButton::clicked, this, &AccountsDialog::onRefreshButtonClicked);
+ connect(ui->refreshButton_Demo, &QPushButton::clicked, this, &AccountsDialog::onRefreshButtonClicked);
+
connect(ui->getFreshCodeButton, &QPushButton::clicked, this, &AccountsDialog::onGetFreshCodeButtonClicked);
QItemSelectionModel *selectionModel = ui->accountListView->selectionModel();
@@ -128,6 +137,9 @@ AccountsDialog::AccountsDialog(QWidget *parent, const QString& internalId) : QDi
connect(ui->createProfileButton, &QCommandLinkButton::clicked, this, &AccountsDialog::onCreateProfileButtonClicked);
setNameStatus(NameStatus::NotSet, QString());
+
+ restoreGeometry(QByteArray::fromBase64(APPLICATION->settings()->get("AccountsDialogGeometry").toByteArray()));
+ ui->splitter->restoreState(QByteArray::fromBase64(APPLICATION->settings()->get("AccountsDialogSplitterState").toByteArray()));
}
AccountsDialog::~AccountsDialog()
@@ -135,6 +147,14 @@ AccountsDialog::~AccountsDialog()
delete ui;
}
+void AccountsDialog::closeEvent(QCloseEvent* event)
+{
+ APPLICATION->settings()->set("AccountsDialogSplitterState", ui->splitter->saveState().toBase64());
+ APPLICATION->settings()->set("AccountsDialogGeometry", saveGeometry().toBase64());
+ QDialog::closeEvent(event);
+}
+
+
void AccountsDialog::onRevertChangesClicked(bool)
{
revertEdits();
@@ -363,6 +383,32 @@ void AccountsDialog::updateStates()
return;
}
+ if(m_currentAccount->accountState() == AccountState::Expired)
+ {
+ ui->accountPageStack->setCurrentWidget(ui->expiredPage);
+ if(m_currentAccount->hasProfile())
+ {
+ ui->selectedAccountLabel_Expired->setText(m_currentAccount->profileName());
+ ui->selectedAccountIconLabel_Expired->setIcon(m_currentAccount->getFace());
+ }
+ else
+ {
+ ui->selectedAccountLabel_Expired->setText(m_currentAccount->gamerTag());
+ ui->selectedAccountIconLabel_Expired->setIcon(APPLICATION->getThemedIcon("noaccount"));
+ }
+ return;
+ }
+
+ // Demo account (no Minecraft entitlement)
+ if(!m_currentAccount->ownsMinecraft())
+ {
+ ui->accountPageStack->setCurrentWidget(ui->demoPage);
+ ui->setupProfilePage->setEnabled(accountIsReady);
+ ui->selectedAccountLabel_Demo->setText(m_currentAccount->gamerTag());
+ ui->selectedAccountIconLabel_Demo->setIcon(APPLICATION->getThemedIcon("noaccount"));
+ return;
+ }
+
// Profile setup page
if(!m_currentAccount->hasProfile())
{
@@ -373,32 +419,31 @@ void AccountsDialog::updateStates()
return;
}
- ui->fullAccountPage->setEnabled(accountIsReady);
-
// Full account page
+ ui->fullAccountPage->setEnabled(accountIsReady);
if(prevAccount != m_currentAccount)
{
revertEdits();
-
- m_capesModel->setAccount(m_currentAccount);
- ui->accountPageStack->setCurrentWidget(ui->fullAccountPage);
- ui->selectedAccountLabel->setText(m_currentAccount->profileName());
- ui->selectedAccountIconLabel->setIcon(m_currentAccount->getFace());
-
- QByteArray playerSkinData = m_currentAccount->getSkin();
- QImage image;
- QString textureID;
- Skins::readSkinFromData(playerSkinData, image, textureID);
- auto maybeEntry = m_skinsModel->skinEntryByTextureID(textureID);
- m_playerSkinState = SkinState{
- m_currentAccount->getCurrentCape(),
- m_currentAccount->getSkinModel(),
- (!maybeEntry.isNull()) ? maybeEntry : Skins::SkinEntry("player", "", image, textureID, playerSkinData)
- };
-
- updateModelToMatchSkin();
- updateSkinDisplay();
}
+
+ m_capesModel->setAccount(m_currentAccount);
+ ui->accountPageStack->setCurrentWidget(ui->fullAccountPage);
+ ui->selectedAccountLabel->setText(m_currentAccount->profileName());
+ ui->selectedAccountIconLabel->setIcon(m_currentAccount->getFace());
+
+ QByteArray playerSkinData = m_currentAccount->getSkin();
+ QImage image;
+ QString textureID;
+ Skins::readSkinFromData(playerSkinData, image, textureID);
+ auto maybeEntry = m_skinsModel->skinEntryByTextureID(textureID);
+ m_playerSkinState = SkinState{
+ m_currentAccount->getCurrentCape(),
+ m_currentAccount->getSkinModel(),
+ (!maybeEntry.isNull()) ? maybeEntry : Skins::SkinEntry("player", "", image, textureID, playerSkinData)
+ };
+
+ updateModelToMatchSkin();
+ updateSkinDisplay();
}
void AccountsDialog::updateModelToMatchSkin()
@@ -639,6 +684,21 @@ void AccountsDialog::onSignOutButtonClicked(bool)
}
}
+void AccountsDialog::onRemoveAndSignInButtonClicked(bool)
+{
+ if(m_currentAccount)
+ {
+ m_accounts->removeAccount(m_currentAccount->internalId());
+ }
+ ui->accountListView->setCurrentIndex(m_accounts->index(0));
+}
+
+void AccountsDialog::onGetMinecraftButtonClicked(bool)
+{
+ DesktopServices::openUrl(QUrl("https://www.minecraft.net/en-us/store/minecraft-java-bedrock-edition-pc"));
+}
+
+
void AccountsDialog::setNameStatus(AccountsDialog::NameStatus status, QString errorString = QString())
{
nameStatus = status;
diff --git a/launcher/ui/dialogs/AccountsDialog.h b/launcher/ui/dialogs/AccountsDialog.h
index 551839e6..1bf4f50e 100644
--- a/launcher/ui/dialogs/AccountsDialog.h
+++ b/launcher/ui/dialogs/AccountsDialog.h
@@ -53,6 +53,9 @@ public:
Error
} nameStatus = NameStatus::NotSet;
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
// Skins stuff
private slots:
void onSkinSelectionChanged(const class QItemSelection &selected, const class QItemSelection &deselected);
@@ -95,6 +98,9 @@ private slots:
// Account display page
void onRefreshButtonClicked(bool);
void onSignOutButtonClicked(bool);
+ void onRemoveAndSignInButtonClicked(bool);
+ void onGetMinecraftButtonClicked(bool);
+
void onAccountChanged(MinecraftAccount * account);
void onAccountActivityChanged(MinecraftAccount * account, bool active);
diff --git a/launcher/ui/dialogs/AccountsDialog.ui b/launcher/ui/dialogs/AccountsDialog.ui
index c02ceadd..60693180 100644
--- a/launcher/ui/dialogs/AccountsDialog.ui
+++ b/launcher/ui/dialogs/AccountsDialog.ui
@@ -6,8 +6,8 @@
0
0
- 1085
- 1022
+ 927
+ 600
@@ -63,7 +63,7 @@
QFrame::Raised
- 0
+ 4
@@ -135,7 +135,7 @@
-
- Refresh
+ Refresh Account
@@ -353,6 +353,12 @@
Qt::ScrollBarAlwaysOff
+
+ QListView::Adjust
+
+
+ 5
+
QListView::IconMode
@@ -539,7 +545,7 @@
-
- Refresh
+ Refresh Account
@@ -635,6 +641,283 @@ Choose your name carefully:
+
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 15
+
+
+
+ Account Name Here
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Sign Out
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ This account's authentication tokens have expired and it needs to be signed in into again.
+
+
+ true
+
+
+ createProfileNameEdit
+
+
+
+ -
+
+
+ Remove and Sign In Again
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 32
+ 32
+
+
+
+
+ 32
+ 32
+
+
+
+
+ -
+
+
+
+ 15
+
+
+
+ Account Name Here
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Refresh Account
+
+
+
+ -
+
+
+ Sign Out
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ QFrame::StyledPanel
+
+
+ QFrame::Raised
+
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+ This account doesn't own Minecraft yet. Without purchasing the game, you can only play the demo.
+
+
+ true
+
+
+ createProfileNameEdit
+
+
+
+ -
+
+
+ Get Minecraft
+
+
+
+
+
+
+
+
+
+
+
-
@@ -667,7 +950,6 @@ Choose your name carefully:
-