aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--launcher/CMakeLists.txt6
-rw-r--r--launcher/LaunchController.cpp37
-rw-r--r--launcher/MainWindow.cpp42
-rw-r--r--launcher/dialogs/ProfileSetupDialog.cpp248
-rw-r--r--launcher/dialogs/ProfileSetupDialog.h88
-rw-r--r--launcher/dialogs/ProfileSetupDialog.ui74
-rw-r--r--launcher/minecraft/auth/AccountData.cpp45
-rw-r--r--launcher/minecraft/auth/AccountData.h7
-rw-r--r--launcher/minecraft/auth/AccountList.cpp38
-rw-r--r--launcher/minecraft/auth/AccountList.h2
-rw-r--r--launcher/minecraft/auth/AuthSession.h1
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.cpp17
-rw-r--r--launcher/minecraft/auth/flows/AuthContext.cpp343
-rw-r--r--launcher/minecraft/auth/flows/AuthContext.h6
-rw-r--r--launcher/minecraft/auth/flows/Parsers.cpp315
-rw-r--r--launcher/minecraft/auth/flows/Parsers.h19
-rw-r--r--launcher/pages/global/AccountListPage.cpp8
17 files changed, 956 insertions, 340 deletions
diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index eba8f8a1..0aeadd51 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -228,6 +228,9 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/Yggdrasil.h
minecraft/auth/flows/Yggdrasil.cpp
+ minecraft/auth/flows/Parsers.h
+ minecraft/auth/flows/Parsers.cpp
+
minecraft/gameoptions/GameOptions.h
minecraft/gameoptions/GameOptions.cpp
@@ -732,6 +735,8 @@ SET(LAUNCHER_SOURCES
dialogs/AboutDialog.h
dialogs/ProfileSelectDialog.cpp
dialogs/ProfileSelectDialog.h
+ dialogs/ProfileSetupDialog.cpp
+ dialogs/ProfileSetupDialog.h
dialogs/CopyInstanceDialog.cpp
dialogs/CopyInstanceDialog.h
dialogs/CustomMessageBox.cpp
@@ -859,6 +864,7 @@ SET(LAUNCHER_UIS
dialogs/ProgressDialog.ui
dialogs/IconPickerDialog.ui
dialogs/ProfileSelectDialog.ui
+ dialogs/ProfileSetupDialog.ui
dialogs/EditAccountDialog.ui
dialogs/ExportInstanceDialog.ui
dialogs/LoginDialog.ui
diff --git a/launcher/LaunchController.cpp b/launcher/LaunchController.cpp
index 1c1e41e6..9edccaf2 100644
--- a/launcher/LaunchController.cpp
+++ b/launcher/LaunchController.cpp
@@ -18,6 +18,7 @@
#include <QHostInfo>
#include <QList>
#include <QHostAddress>
+#include "dialogs/ProfileSetupDialog.h"
LaunchController::LaunchController(QObject *parent) : Task(parent)
{
@@ -79,7 +80,7 @@ void LaunchController::decideAccount()
// If the user said to use the account as default, do that.
if (selectDialog.useAsGlobalDefault() && m_accountToUse) {
- accounts->setActiveAccount(m_accountToUse->profileId());
+ accounts->setActiveAccount(m_accountToUse);
}
}
}
@@ -179,10 +180,40 @@ void LaunchController::login() {
}
break;
}
+ case AuthSession::RequiresProfileSetup: {
+ auto entitlement = m_accountToUse->accountData()->minecraftEntitlement;
+ QString errorString;
+ if(!entitlement.canPlayMinecraft) {
+ errorString = tr("The account does not own Minecraft. You need to purchase the game first to play it.");
+ QMessageBox::warning(
+ nullptr,
+ tr("Missing Minecraft profile"),
+ errorString,
+ QMessageBox::StandardButton::Ok,
+ QMessageBox::StandardButton::Ok
+ );
+ tryagain = false;
+ emitFailed(errorString);
+ return;
+ }
+ // Now handle setting up a profile name here...
+ ProfileSetupDialog dialog(m_accountToUse, m_parentWidget);
+ if (dialog.exec() == QDialog::Accepted)
+ {
+ tryagain = true;
+ continue;
+ }
+ else
+ {
+ tryagain = false;
+ emitFailed(tr("Received undetermined session status during login."));
+ return;
+ }
+ }
case AuthSession::RequiresOAuth: {
auto errorString = tr("Microsoft account has expired and needs to be logged into manually again.");
QMessageBox::warning(
- nullptr,
+ m_parentWidget,
tr("Microsoft Account refresh failed"),
errorString,
QMessageBox::StandardButton::Ok,
@@ -195,7 +226,7 @@ void LaunchController::login() {
case AuthSession::GoneOrMigrated: {
auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to.");
QMessageBox::warning(
- nullptr,
+ m_parentWidget,
tr("Account gone"),
errorString,
QMessageBox::StandardButton::Ok,
diff --git a/launcher/MainWindow.cpp b/launcher/MainWindow.cpp
index 21502894..7df3e717 100644
--- a/launcher/MainWindow.cpp
+++ b/launcher/MainWindow.cpp
@@ -1030,7 +1030,6 @@ void MainWindow::repopulateAccountsMenu()
QString active_profileId = "";
if (active_account != nullptr)
{
- active_profileId = active_account->profileId();
// this can be called before accountMenuButton exists
if (accountMenuButton)
{
@@ -1053,14 +1052,20 @@ void MainWindow::repopulateAccountsMenu()
MinecraftAccountPtr account = accounts->at(i);
auto profileLabel = profileInUseFilter(account->profileName(), account->isInUse());
QAction *action = new QAction(profileLabel, this);
- action->setData(account->profileId());
+ action->setData(i);
action->setCheckable(true);
- if (active_profileId == account->profileId())
+ if (active_account == account)
{
action->setChecked(true);
}
- action->setIcon(account->getFace());
+ auto face = account->getFace();
+ if(!face.isNull()) {
+ action->setIcon(face);
+ }
+ else {
+ action->setIcon(LAUNCHER->getThemedIcon("noaccount"));
+ }
accountMenu->addAction(action);
connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
}
@@ -1071,8 +1076,8 @@ void MainWindow::repopulateAccountsMenu()
QAction *action = new QAction(tr("No Default Account"), this);
action->setCheckable(true);
action->setIcon(LAUNCHER->getThemedIcon("noaccount"));
- action->setData("");
- if (active_profileId.isEmpty()) {
+ action->setData(-1);
+ if (active_account == nullptr) {
action->setChecked(true);
}
@@ -1098,20 +1103,19 @@ void MainWindow::updatesAllowedChanged(bool allowed)
void MainWindow::changeActiveAccount()
{
QAction *sAction = (QAction *)sender();
+
// Profile's associated Mojang username
- // Will need to change when profiles are properly implemented
- if (sAction->data().type() != QVariant::Type::String)
+ if (sAction->data().type() != QVariant::Type::Int)
return;
QVariant data = sAction->data();
- QString id = "";
- if (!data.isNull())
- {
- id = data.toString();
+ bool valid = false;
+ int index = data.toInt(&valid);
+ if(!valid) {
+ index = -1;
}
-
- LAUNCHER->accounts()->setActiveAccount(id);
-
+ std::shared_ptr<AccountList> accounts = LAUNCHER->accounts();
+ accounts->setActiveAccount(index == -1 ? nullptr : accounts->at(index));
activeAccountChanged();
}
@@ -1126,7 +1130,13 @@ void MainWindow::activeAccountChanged()
{
auto profileLabel = profileInUseFilter(account->profileName(), account->isInUse());
accountMenuButton->setText(profileLabel);
- accountMenuButton->setIcon(account->getFace());
+ auto face = account->getFace();
+ if(face.isNull()) {
+ accountMenuButton->setIcon(LAUNCHER->getThemedIcon("noaccount"));
+ }
+ else {
+ accountMenuButton->setIcon(face);
+ }
return;
}
diff --git a/launcher/dialogs/ProfileSetupDialog.cpp b/launcher/dialogs/ProfileSetupDialog.cpp
new file mode 100644
index 00000000..e3d7aec2
--- /dev/null
+++ b/launcher/dialogs/ProfileSetupDialog.cpp
@@ -0,0 +1,248 @@
+/* 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 "ProfileSetupDialog.h"
+#include "ui_ProfileSetupDialog.h"
+
+#include <QPushButton>
+#include <QAction>
+#include <QRegExpValidator>
+#include <QDebug>
+
+#include <dialogs/ProgressDialog.h>
+
+#include <Launcher.h>
+#include <minecraft/auth/flows/AuthRequest.h>
+#include <minecraft/auth/flows/Parsers.h>
+
+ProfileSetupDialog::ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget *parent)
+ : QDialog(parent), m_accountToSetup(accountToSetup), ui(new Ui::ProfileSetupDialog)
+{
+ ui->setupUi(this);
+ ui->errorLabel->setVisible(false);
+
+ goodIcon = LAUNCHER->getThemedIcon("status-good");
+ yellowIcon = LAUNCHER->getThemedIcon("status-yellow");
+ badIcon = LAUNCHER->getThemedIcon("status-bad");
+
+ QRegExp permittedNames("[a-zA-Z0-9_]{3,16}");
+ auto nameEdit = ui->nameEdit;
+ nameEdit->setValidator(new QRegExpValidator(permittedNames));
+ nameEdit->setClearButtonEnabled(true);
+ validityAction = nameEdit->addAction(yellowIcon, QLineEdit::LeadingPosition);
+ connect(nameEdit, &QLineEdit::textEdited, this, &ProfileSetupDialog::nameEdited);
+
+ checkStartTimer.setSingleShot(true);
+ connect(&checkStartTimer, &QTimer::timeout, this, &ProfileSetupDialog::startCheck);
+
+ setNameStatus(NameStatus::NotSet, QString());
+}
+
+ProfileSetupDialog::~ProfileSetupDialog()
+{
+ delete ui;
+}
+
+void ProfileSetupDialog::on_buttonBox_accepted()
+{
+ setupProfile(currentCheck);
+}
+
+void ProfileSetupDialog::on_buttonBox_rejected()
+{
+ reject();
+}
+
+void ProfileSetupDialog::setNameStatus(ProfileSetupDialog::NameStatus status, QString errorString = QString())
+{
+ nameStatus = status;
+ auto okButton = ui->buttonBox->button(QDialogButtonBox::Ok);
+ switch(nameStatus)
+ {
+ case NameStatus::Available: {
+ validityAction->setIcon(goodIcon);
+ okButton->setEnabled(true);
+ }
+ break;
+ case NameStatus::NotSet:
+ case NameStatus::Pending:
+ validityAction->setIcon(yellowIcon);
+ okButton->setEnabled(false);
+ break;
+ case NameStatus::Exists:
+ case NameStatus::Error:
+ validityAction->setIcon(badIcon);
+ okButton->setEnabled(false);
+ break;
+ }
+ if(!errorString.isEmpty()) {
+ ui->errorLabel->setText(errorString);
+ ui->errorLabel->setVisible(true);
+ }
+ else {
+ ui->errorLabel->setVisible(false);
+ }
+}
+
+void ProfileSetupDialog::nameEdited(const QString& name)
+{
+ if(!ui->nameEdit->hasAcceptableInput()) {
+ setNameStatus(NameStatus::NotSet, tr("Name is too short - must be between 3 and 16 characters long."));
+ return;
+ }
+ scheduleCheck(name);
+}
+
+void ProfileSetupDialog::scheduleCheck(const QString& name) {
+ queuedCheck = name;
+ setNameStatus(NameStatus::Pending);
+ checkStartTimer.start(1000);
+}
+
+void ProfileSetupDialog::startCheck() {
+ if(isChecking) {
+ return;
+ }
+ if(queuedCheck.isNull()) {
+ return;
+ }
+ checkName(queuedCheck);
+}
+
+
+void ProfileSetupDialog::checkName(const QString &name) {
+ if(isChecking) {
+ return;
+ }
+
+ currentCheck = name;
+ isChecking = true;
+
+ auto token = m_accountToSetup->accessToken();
+
+ auto url = QString("https://api.minecraftservices.com/minecraft/profile/name/%1/available").arg(name);
+ QNetworkRequest request = QNetworkRequest(url);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ request.setRawHeader("Accept", "application/json");
+ request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8());
+
+ AuthRequest *requestor = new AuthRequest(this);
+ connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::checkFinished);
+ requestor->get(request);
+}
+
+void ProfileSetupDialog::checkFinished(
+ QNetworkReply::NetworkError error,
+ QByteArray data,
+ QList<QNetworkReply::RawHeaderPair> headers
+) {
+ if(error == QNetworkReply::NoError) {
+ auto doc = QJsonDocument::fromJson(data);
+ auto root = doc.object();
+ auto statusValue = root.value("status").toString("INVALID");
+ if(statusValue == "AVAILABLE") {
+ setNameStatus(NameStatus::Available);
+ }
+ else if (statusValue == "DUPLICATE") {
+ setNameStatus(NameStatus::Exists, tr("Minecraft profile with name %1 already exists.").arg(currentCheck));
+ }
+ else if (statusValue == "NOT_ALLOWED") {
+ setNameStatus(NameStatus::Exists, tr("The name %1 is not allowed.").arg(currentCheck));
+ }
+ else {
+ setNameStatus(NameStatus::Error, tr("Unhandled profile name status: %1").arg(statusValue));
+ }
+ }
+ else {
+ setNameStatus(NameStatus::Error, tr("Failed to check name availability."));
+ }
+ isChecking = false;
+}
+
+void ProfileSetupDialog::setupProfile(const QString &profileName) {
+ if(isWorking) {
+ return;
+ }
+
+ auto token = m_accountToSetup->accessToken();
+
+ auto url = QString("https://api.minecraftservices.com/minecraft/profile");
+ QNetworkRequest request = QNetworkRequest(url);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+ request.setRawHeader("Accept", "application/json");
+ request.setRawHeader("Authorization", QString("Bearer %1").arg(token).toUtf8());
+
+ QString payloadTemplate("{\"profileName\":\"%1\"}");
+ auto data = payloadTemplate.arg(profileName).toUtf8();
+
+ AuthRequest *requestor = new AuthRequest(this);
+ connect(requestor, &AuthRequest::finished, this, &ProfileSetupDialog::setupProfileFinished);
+ requestor->post(request, data);
+ isWorking = true;
+
+ auto button = ui->buttonBox->button(QDialogButtonBox::Cancel);
+ button->setEnabled(false);
+}
+
+namespace {
+
+struct MojangError{
+ static MojangError fromJSON(QByteArray data) {
+ MojangError out;
+ out.error = QString::fromUtf8(data);
+ auto doc = QJsonDocument::fromJson(data, &out.parseError);
+ auto object = doc.object();
+
+ out.fullyParsed = true;
+ out.fullyParsed &= Parsers::getString(object.value("path"), out.path);
+ out.fullyParsed &= Parsers::getString(object.value("error"), out.error);
+ out.fullyParsed &= Parsers::getString(object.value("errorMessage"), out.errorMessage);
+
+ return out;
+ }
+
+ QString rawError;
+ QJsonParseError parseError;
+ bool fullyParsed;
+
+ QString path;
+ QString error;
+ QString errorMessage;
+};
+
+}
+
+void ProfileSetupDialog::setupProfileFinished(
+ QNetworkReply::NetworkError error,
+ QByteArray data,
+ QList<QNetworkReply::RawHeaderPair> headers
+) {
+ isWorking = false;
+ if(error == QNetworkReply::NoError) {
+ /*
+ * data contains the profile in the response
+ * ... we could parse it and update the account, but let's just return back to the normal login flow instead...
+ */
+ accept();
+ }
+ else {
+ auto parsedError = MojangError::fromJSON(data);
+ ui->errorLabel->setVisible(true);
+ ui->errorLabel->setText(tr("The server returned the following error:") + "\n\n" + parsedError.errorMessage);
+ qDebug() << parsedError.rawError;
+ auto button = ui->buttonBox->button(QDialogButtonBox::Cancel);
+ button->setEnabled(true);
+ }
+}
diff --git a/launcher/dialogs/ProfileSetupDialog.h b/launcher/dialogs/ProfileSetupDialog.h
new file mode 100644
index 00000000..6f413ebd
--- /dev/null
+++ b/launcher/dialogs/ProfileSetupDialog.h
@@ -0,0 +1,88 @@
+/* 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 <QDialog>
+#include <QIcon>
+#include <QTimer>
+#include <QNetworkReply>
+
+#include <memory>
+#include <minecraft/auth/MinecraftAccount.h>
+
+namespace Ui
+{
+class ProfileSetupDialog;
+}
+
+class ProfileSetupDialog : public QDialog
+{
+ Q_OBJECT
+public:
+
+ explicit ProfileSetupDialog(MinecraftAccountPtr accountToSetup, QWidget *parent = 0);
+ ~ProfileSetupDialog();
+
+ enum class NameStatus
+ {
+ NotSet,
+ Pending,
+ Available,
+ Exists,
+ Error
+ } nameStatus = NameStatus::NotSet;
+
+private slots:
+ void on_buttonBox_accepted();
+ void on_buttonBox_rejected();
+
+ void nameEdited(const QString &name);
+ void checkFinished(
+ QNetworkReply::NetworkError error,
+ QByteArray data,
+ QList<QNetworkReply::RawHeaderPair> headers
+ );
+ void startCheck();
+
+ void setupProfileFinished(
+ QNetworkReply::NetworkError error,
+ QByteArray data,
+ QList<QNetworkReply::RawHeaderPair> headers
+ );
+protected:
+ void scheduleCheck(const QString &name);
+ void checkName(const QString &name);
+ void setNameStatus(NameStatus status, QString errorString);
+
+ void setupProfile(const QString & profileName);
+
+private:
+ MinecraftAccountPtr m_accountToSetup;
+ Ui::ProfileSetupDialog *ui;
+ QIcon goodIcon;
+ QIcon yellowIcon;
+ QIcon badIcon;
+ QAction * validityAction = nullptr;
+
+ QString queuedCheck;
+
+ bool isChecking = false;
+ bool isWorking = false;
+ QString currentCheck;
+
+ QTimer checkStartTimer;
+};
+
diff --git a/launcher/dialogs/ProfileSetupDialog.ui b/launcher/dialogs/ProfileSetupDialog.ui
new file mode 100644
index 00000000..9dbabb4b
--- /dev/null
+++ b/launcher/dialogs/ProfileSetupDialog.ui
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ProfileSetupDialog</class>
+ <widget class="QDialog" name="ProfileSetupDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>615</width>
+ <height>208</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Choose Minecraft name</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="0" colspan="2">
+ <widget class="QLabel" name="descriptionLabel">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>You just need to take one more step to be able to play Minecraft on this account.
+
+Choose your name carefully:</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="buddy">
+ <cstring>nameEdit</cstring>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLineEdit" name="nameEdit"/>
+ </item>
+ <item row="4" column="0" colspan="2">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="errorLabel">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="text">
+ <string notr="true">Errors go here</string>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ <property name="openExternalLinks">
+ <bool>true</bool>
+ </property>
+ <property name="textInteractionFlags">
+ <set>Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <tabstops>
+ <tabstop>nameEdit</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/launcher/minecraft/auth/AccountData.cpp b/launcher/minecraft/auth/AccountData.cpp
index 5c6de9df..8aa4e37f 100644
--- a/launcher/minecraft/auth/AccountData.cpp
+++ b/launcher/minecraft/auth/AccountData.cpp
@@ -207,6 +207,35 @@ MinecraftProfile profileFromJSONV3(const QJsonObject &parent, const char * token
return out;
}
+void entitlementToJSONV3(QJsonObject &parent, MinecraftEntitlement p) {
+ if(p.validity == Katabasis::Validity::None) {
+ return;
+ }
+ QJsonObject out;
+ out["ownsMinecraft"] = QJsonValue(p.ownsMinecraft);
+ out["canPlayMinecraft"] = QJsonValue(p.canPlayMinecraft);
+ parent["entitlement"] = out;
+}
+
+bool entitlementFromJSONV3(const QJsonObject &parent, MinecraftEntitlement & out) {
+ auto entitlementObject = parent.value("entitlement").toObject();
+ if(entitlementObject.isEmpty()) {
+ return false;
+ }
+ {
+ auto ownsMinecraftV = entitlementObject.value("ownsMinecraft");
+ auto canPlayMinecraftV = entitlementObject.value("canPlayMinecraft");
+ if(!ownsMinecraftV.isBool() || !canPlayMinecraftV.isBool()) {
+ qWarning() << "mandatory attributes are missing or of unexpected type";
+ return false;
+ }
+ out.canPlayMinecraft = canPlayMinecraftV.toBool(false);
+ out.ownsMinecraft = ownsMinecraftV.toBool(false);
+ out.validity = Katabasis::Validity::Assumed;
+ }
+ return true;
+}
+
}
bool AccountData::resumeStateFromV2(QJsonObject data) {
@@ -304,9 +333,15 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
yggdrasilToken = tokenFromJSONV3(data, "ygg");
minecraftProfile = profileFromJSONV3(data, "profile");
+ if(!entitlementFromJSONV3(data, minecraftEntitlement)) {
+ if(minecraftProfile.validity != Katabasis::Validity::None) {
+ minecraftEntitlement.canPlayMinecraft = true;
+ minecraftEntitlement.ownsMinecraft = true;
+ minecraftEntitlement.validity = Katabasis::Validity::Assumed;
+ }
+ }
validity_ = minecraftProfile.validity;
-
return true;
}
@@ -331,6 +366,7 @@ QJsonObject AccountData::saveState() const {
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
+ entitlementToJSONV3(output, minecraftEntitlement);
return output;
}
@@ -378,7 +414,12 @@ QString AccountData::profileId() const {
}
QString AccountData::profileName() const {
- return minecraftProfile.name;
+ if(minecraftProfile.name.size() == 0) {
+ return QObject::tr("No profile (%1)").arg(accountDisplayString());
+ }
+ else {
+ return minecraftProfile.name;
+ }
}
QString AccountData::accountDisplayString() const {
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index cf58fb76..09cd2c73 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -21,6 +21,12 @@ struct Cape {
QByteArray data;
};
+struct MinecraftEntitlement {
+ bool ownsMinecraft = false;
+ bool canPlayMinecraft = false;
+ Katabasis::Validity validity = Katabasis::Validity::None;
+};
+
struct MinecraftProfile {
QString id;
QString name;
@@ -69,5 +75,6 @@ struct AccountData {
Katabasis::Token yggdrasilToken;
MinecraftProfile minecraftProfile;
+ MinecraftEntitlement minecraftEntitlement;
Katabasis::Validity validity_ = Katabasis::Validity::None;
};
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index a76cac55..4895c39c 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -64,21 +64,18 @@ const MinecraftAccountPtr AccountList::at(int i) const
void AccountList::addAccount(const MinecraftAccountPtr account)
{
- // We only ever want accounts with valid profiles.
- // Keeping profile-less accounts is pointless and serves no purpose.
auto profileId = account->profileId();
- if(!profileId.size()) {
- return;
+ if(profileId.size()) {
+ // override/replace existing account with the same profileId
+ auto existingAccount = findAccountByProfileId(profileId);
+ if(existingAccount != -1) {
+ m_accounts[existingAccount] = account;
+ emit dataChanged(index(existingAccount), index(existingAccount, columnCount(QModelIndex()) - 1));
+ onListChanged();
+ return;
+ }
}
- // override/replace existing account with the same profileId
- auto existingAccount = findAccountByProfileId(profileId);
- if(existingAccount != -1) {
- m_accounts[existingAccount] = account;
- emit dataChanged(index(existingAccount), index(existingAccount, columnCount(QModelIndex()) - 1));
- onListChanged();
- return;
- }
// if we don't have this porfileId yet, add the account to the end
int row = m_accounts.count();
@@ -112,9 +109,9 @@ MinecraftAccountPtr AccountList::activeAccount() const
return m_activeAccount;
}
-void AccountList::setActiveAccount(const QString &profileId)
+void AccountList::setActiveAccount(MinecraftAccountPtr newAccount)
{
- if (profileId.isEmpty() && m_activeAccount)
+ if (!newAccount && m_activeAccount)
{
int idx = 0;
auto prevActiveAcc = m_activeAccount;
@@ -138,7 +135,7 @@ void AccountList::setActiveAccount(const QString &profileId)
int idx = 0;
for (MinecraftAccountPtr account : m_accounts)
{
- if (account->profileId() == profileId)
+ if (account == newAccount)
{
newActiveAccount = account;
newActiveAccountIdx = idx;
@@ -321,7 +318,7 @@ bool AccountList::setData(const QModelIndex &index, const QVariant &value, int r
if(value == Qt::Checked)
{
MinecraftAccountPtr account = at(index.row());
- setActiveAccount(account->profileId());
+ setActiveAccount(account);
}
}
@@ -435,11 +432,10 @@ bool AccountList::loadV3(QJsonObject& root) {
if (account.get() != nullptr)
{
auto profileId = account->profileId();
- if(!profileId.size()) {
- continue;
- }
- if(findAccountByProfileId(profileId) != -1) {
- continue;
+ if(profileId.size()) {
+ if(findAccountByProfileId(profileId) != -1) {
+ continue;
+ }
}
connect(account.get(), &MinecraftAccount::changed, this, &AccountList::accountChanged);
m_accounts.append(account);
diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h
index e275eb17..d6d72cd3 100644
--- a/launcher/minecraft/auth/AccountList.h
+++ b/launcher/minecraft/auth/AccountList.h
@@ -79,7 +79,7 @@ public:
bool saveList();
MinecraftAccountPtr activeAccount() const;
- void setActiveAccount(const QString &profileId);
+ void setActiveAccount(MinecraftAccountPtr profileId);
bool anyAccountIsValid();
signals:
diff --git a/launcher/minecraft/auth/AuthSession.h b/launcher/minecraft/auth/AuthSession.h
index f609d5d3..f631a97d 100644
--- a/launcher/minecraft/auth/AuthSession.h
+++ b/launcher/minecraft/auth/AuthSession.h
@@ -17,6 +17,7 @@ struct AuthSession
Undetermined,
RequiresOAuth,
RequiresPassword,
+ RequiresProfileSetup,
PlayableOffline,
PlayableOnline,
GoneOrMigrated
diff --git a/launcher/minecraft/auth/MinecraftAccount.cpp b/launcher/minecraft/auth/MinecraftAccount.cpp
index 2d76f9ac..8e294443 100644
--- a/launcher/minecraft/auth/MinecraftAccount.cpp
+++ b/launcher/minecraft/auth/MinecraftAccount.cpp
@@ -213,8 +213,21 @@ void MinecraftAccount::authSucceeded()
auto session = m_currentTask->getAssignedSession();
if (session)
{
- session->status =
- session->wants_online ? AuthSession::PlayableOnline : AuthSession::PlayableOffline;
+ /*
+ session->status = AuthSession::RequiresProfileSetup;
+ session->auth_server_online = true;
+ */
+ if(data.profileId().size() == 0) {
+ session->status = AuthSession::RequiresProfileSetup;
+ }
+ else {
+ if(session->wants_online) {
+ session->status = AuthSession::PlayableOnline;
+ }
+ else {
+ session->status = AuthSession::PlayableOffline;
+ }
+ }
fillSession(session);
session->auth_server_online = true;
}
diff --git a/launcher/minecraft/auth/flows/AuthContext.cpp b/launcher/minecraft/auth/flows/AuthContext.cpp
index 9fb3ec48..17fcc9dc 100644
--- a/launcher/minecraft/auth/flows/AuthContext.cpp
+++ b/launcher/minecraft/auth/flows/AuthContext.cpp
@@ -22,6 +22,8 @@
#include "Env.h"
+#include "Parsers.h"
+
using OAuth2 = Katabasis::OAuth2;
using Activity = Katabasis::Activity;
@@ -86,7 +88,7 @@ void AuthContext::initMojang() {
}
void AuthContext::onMoj