aboutsummaryrefslogtreecommitdiff
path: root/launcher/minecraft
diff options
context:
space:
mode:
Diffstat (limited to 'launcher/minecraft')
-rw-r--r--launcher/minecraft/auth/AccountData.h15
-rw-r--r--launcher/minecraft/auth/AccountList.cpp138
-rw-r--r--launcher/minecraft/auth/AccountList.h28
-rw-r--r--launcher/minecraft/auth/AccountTask.cpp75
-rw-r--r--launcher/minecraft/auth/AccountTask.h72
-rw-r--r--launcher/minecraft/auth/AuthRequest.cpp (renamed from launcher/minecraft/auth/flows/AuthRequest.cpp)10
-rw-r--r--launcher/minecraft/auth/AuthRequest.h (renamed from launcher/minecraft/auth/flows/AuthRequest.h)8
-rw-r--r--launcher/minecraft/auth/AuthStep.cpp7
-rw-r--r--launcher/minecraft/auth/AuthStep.h33
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.cpp259
-rw-r--r--launcher/minecraft/auth/MinecraftAccount.h37
-rw-r--r--launcher/minecraft/auth/Parsers.cpp (renamed from launcher/minecraft/auth/flows/Parsers.cpp)2
-rw-r--r--launcher/minecraft/auth/Parsers.h (renamed from launcher/minecraft/auth/flows/Parsers.h)4
-rw-r--r--launcher/minecraft/auth/Yggdrasil.cpp (renamed from launcher/minecraft/auth/flows/Yggdrasil.cpp)34
-rw-r--r--launcher/minecraft/auth/Yggdrasil.h (renamed from launcher/minecraft/auth/flows/Yggdrasil.h)22
-rw-r--r--launcher/minecraft/auth/flows/AuthContext.cpp671
-rw-r--r--launcher/minecraft/auth/flows/AuthContext.h110
-rw-r--r--launcher/minecraft/auth/flows/AuthFlow.cpp71
-rw-r--r--launcher/minecraft/auth/flows/AuthFlow.h45
-rw-r--r--launcher/minecraft/auth/flows/MSA.cpp37
-rw-r--r--launcher/minecraft/auth/flows/MSA.h22
-rw-r--r--launcher/minecraft/auth/flows/MSAInteractive.cpp22
-rw-r--r--launcher/minecraft/auth/flows/MSAInteractive.h13
-rw-r--r--launcher/minecraft/auth/flows/MSASilent.cpp16
-rw-r--r--launcher/minecraft/auth/flows/MSASilent.h13
-rw-r--r--launcher/minecraft/auth/flows/Mojang.cpp27
-rw-r--r--launcher/minecraft/auth/flows/Mojang.h26
-rw-r--r--launcher/minecraft/auth/flows/MojangLogin.cpp18
-rw-r--r--launcher/minecraft/auth/flows/MojangLogin.h17
-rw-r--r--launcher/minecraft/auth/flows/MojangRefresh.cpp17
-rw-r--r--launcher/minecraft/auth/flows/MojangRefresh.h10
-rw-r--r--launcher/minecraft/auth/steps/EntitlementsStep.cpp53
-rw-r--r--launcher/minecraft/auth/steps/EntitlementsStep.h25
-rw-r--r--launcher/minecraft/auth/steps/GetSkinStep.cpp43
-rw-r--r--launcher/minecraft/auth/steps/GetSkinStep.h22
-rw-r--r--launcher/minecraft/auth/steps/LauncherLoginStep.cpp78
-rw-r--r--launcher/minecraft/auth/steps/LauncherLoginStep.h22
-rw-r--r--launcher/minecraft/auth/steps/MSAStep.cpp111
-rw-r--r--launcher/minecraft/auth/steps/MSAStep.h32
-rw-r--r--launcher/minecraft/auth/steps/MigrationEligibilityStep.cpp45
-rw-r--r--launcher/minecraft/auth/steps/MigrationEligibilityStep.h22
-rw-r--r--launcher/minecraft/auth/steps/MinecraftProfileStep.cpp83
-rw-r--r--launcher/minecraft/auth/steps/MinecraftProfileStep.h22
-rw-r--r--launcher/minecraft/auth/steps/XboxAuthorizationStep.cpp158
-rw-r--r--launcher/minecraft/auth/steps/XboxAuthorizationStep.h34
-rw-r--r--launcher/minecraft/auth/steps/XboxProfileStep.cpp73
-rw-r--r--launcher/minecraft/auth/steps/XboxProfileStep.h22
-rw-r--r--launcher/minecraft/auth/steps/XboxUserStep.cpp68
-rw-r--r--launcher/minecraft/auth/steps/XboxUserStep.h22
-rw-r--r--launcher/minecraft/auth/steps/YggdrasilStep.cpp51
-rw-r--r--launcher/minecraft/auth/steps/YggdrasilStep.h28
-rw-r--r--launcher/minecraft/services/CapeChange.cpp8
-rw-r--r--launcher/minecraft/services/CapeChange.h5
-rw-r--r--launcher/minecraft/services/SkinDelete.cpp6
-rw-r--r--launcher/minecraft/services/SkinDelete.h6
-rw-r--r--launcher/minecraft/services/SkinUpload.cpp6
-rw-r--r--launcher/minecraft/services/SkinUpload.h5
57 files changed, 1681 insertions, 1248 deletions
diff --git a/launcher/minecraft/auth/AccountData.h b/launcher/minecraft/auth/AccountData.h
index 09cd2c73..fa42747e 100644
--- a/launcher/minecraft/auth/AccountData.h
+++ b/launcher/minecraft/auth/AccountData.h
@@ -41,6 +41,16 @@ enum class AccountType {
Mojang
};
+enum class AccountState {
+ Unchecked,
+ Offline,
+ Working,
+ Online,
+ Errored,
+ Expired,
+ Gone
+};
+
struct AccountData {
QJsonObject saveState() const;
bool resumeStateFromV2(QJsonObject data);
@@ -77,4 +87,9 @@ struct AccountData {
MinecraftProfile minecraftProfile;
MinecraftEntitlement minecraftEntitlement;
Katabasis::Validity validity_ = Katabasis::Validity::None;
+
+ // runtime only information (not saved with the account)
+ QString internalId;
+ QString errorString;
+ AccountState accountState = AccountState::Unchecked;
};
diff --git a/launcher/minecraft/auth/AccountList.cpp b/launcher/minecraft/auth/AccountList.cpp
index d7537345..c44e3e89 100644
--- a/launcher/minecraft/auth/AccountList.cpp
+++ b/launcher/minecraft/auth/AccountList.cpp
@@ -15,6 +15,7 @@
#include "AccountList.h"
#include "AccountData.h"
+#include "AccountTask.h"
#include <QIODevice>
#include <QFile>
@@ -24,6 +25,7 @@
#include <QJsonObject>
#include <QJsonParseError>
#include <QDir>
+#include <QTimer>
#include <QDebug>
@@ -35,7 +37,14 @@ enum AccountListVersion {
MojangMSA = 3
};
-AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) { }
+AccountList::AccountList(QObject *parent) : QAbstractListModel(parent) {
+ m_refreshTimer = new QTimer(this);
+ m_refreshTimer->setSingleShot(true);
+ connect(m_refreshTimer, &QTimer::timeout, this, &AccountList::fillQueue);
+ m_nextTimer = new QTimer(this);
+ m_nextTimer->setSingleShot(true);
+ connect(m_nextTimer, &QTimer::timeout, this, &AccountList::tryNext);
+}
AccountList::~AccountList() noexcept {}
@@ -244,13 +253,29 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
}
case StatusColumn: {
- if(account->isActive()) {
- return tr("Working", "Account status");
- }
- if(account->isExpired()) {
- return tr("Expired", "Account status");
+ switch(account->accountState()) {
+ case AccountState::Unchecked: {
+ return tr("Unchecked", "Account status");
+ }
+ case AccountState::Offline: {
+ return tr("Offline", "Account status");
+ }
+ case AccountState::Online: {
+ return tr("Online", "Account status");
+ }
+ case AccountState::Working: {
+ return tr("Working", "Account status");
+ }
+ case AccountState::Errored: {
+ return tr("Errored", "Account status");
+ }
+ case AccountState::Expired: {
+ return tr("Expired", "Account status");
+ }
+ case AccountState::Gone: {
+ return tr("Gone", "Account status");
+ }
}
- return tr("Ready", "Account status");
}
case ProfileNameColumn: {
@@ -583,10 +608,105 @@ void AccountList::setListFilePath(QString path, bool autosave)
bool AccountList::anyAccountIsValid()
{
- for(auto account:m_accounts)
+ for(auto account: m_accounts)
{
- if(account->accountStatus() != NotVerified)
+ if(account->ownsMinecraft()) {
return true;
+ }
}
return false;
}
+
+void AccountList::fillQueue() {
+
+ if(m_defaultAccount && m_defaultAccount->shouldRefresh()) {
+ auto idToRefresh = m_defaultAccount->internalId();
+ m_refreshQueue.push_back(idToRefresh);
+ qDebug() << "AccountList: Queued default account with internal ID " << idToRefresh << " to refresh first";
+ }
+
+ for(int i = 0; i < count(); i++) {
+ auto account = at(i);
+ if(account == m_defaultAccount) {
+ continue;
+ }
+
+ if(account->shouldRefresh()) {
+ auto idToRefresh = account->internalId();
+ m_refreshQueue.push_back(idToRefresh);
+ qDebug() << "AccountList: Queued account with internal ID " << idToRefresh << " to refresh";
+ }
+ }
+ m_refreshQueue.removeDuplicates();
+ tryNext();
+}
+
+void AccountList::requestRefresh(QString accountId) {
+ m_refreshQueue.push_back(accountId);
+ if(!isActive()) {
+ tryNext();
+ }
+}
+
+void AccountList::tryNext() {
+ beginActivity();
+ while (m_refreshQueue.length()) {
+ auto accountId = m_refreshQueue.front();
+ m_refreshQueue.pop_front();
+ for(int i = 0; i < count(); i++) {
+ auto account = at(i);
+ if(account->internalId() == accountId) {
+ m_currentTask = account->refresh();
+ if(m_currentTask) {
+ connect(m_currentTask.get(), &AccountTask::succeeded, this, &AccountList::authSucceeded);
+ connect(m_currentTask.get(), &AccountTask::failed, this, &AccountList::authFailed);
+ m_currentTask->start();
+ qDebug() << "RefreshSchedule: Processing account " << account->accountDisplayString() << " with internal ID " << accountId;
+ return;
+ }
+ }
+ }
+ qDebug() << "RefreshSchedule: Account with with internal ID " << accountId << " not found.";
+ }
+ endActivity();
+ // if we get here, no account needed refreshing. Schedule refresh in an hour.
+ m_refreshTimer->start(std::chrono::hours(1));
+}
+
+void AccountList::authSucceeded() {
+ qDebug() << "RefreshSchedule: Background account refresh succeeded";
+ m_currentTask.reset();
+ endActivity();
+ m_nextTimer->start(std::chrono::seconds(20));
+}
+
+void AccountList::authFailed(QString reason) {
+ qDebug() << "RefreshSchedule: Background account refresh failed: " << reason;
+ m_currentTask.reset();
+ endActivity();
+ m_nextTimer->start(std::chrono::seconds(20));
+}
+
+bool AccountList::isActive() const {
+ return m_activityCount != 0;
+}
+
+void AccountList::beginActivity() {
+ bool activating = m_activityCount == 0;
+ m_activityCount++;
+ if(activating) {
+ emit activityChanged(true);
+ }
+}
+
+void AccountList::endActivity() {
+ if(m_activityCount == 0) {
+ qWarning() << m_name << " - Activity count would become below zero";
+ return;
+ }
+ bool deactivating = m_activityCount == 1;
+ m_activityCount--;
+ if(deactivating) {
+ emit activityChanged(false);
+ }
+}
diff --git a/launcher/minecraft/auth/AccountList.h b/launcher/minecraft/auth/AccountList.h
index 08004628..75686c21 100644
--- a/launcher/minecraft/auth/AccountList.h
+++ b/launcher/minecraft/auth/AccountList.h
@@ -67,6 +67,8 @@ public:
MinecraftAccountPtr getAccountByProfileName(const QString &profileName) const;
QStringList profileNames() const;
+ void requestRefresh(QString accountId);
+
/*!
* Sets the path to load/save the list file from/to.
* If autosave is true, this list will automatically save to the given path whenever it changes.
@@ -85,10 +87,20 @@ public:
void setDefaultAccount(MinecraftAccountPtr profileId);
bool anyAccountIsValid();
+ bool isActive() const;
+
+protected:
+ void beginActivity();
+ void endActivity();
+
+private:
+ const char* m_name;
+ uint32_t m_activityCount = 0;
signals:
void listChanged();
void listActivityChanged();
void defaultAccountChanged();
+ void activityChanged(bool active);
public slots:
/**
@@ -101,7 +113,23 @@ public slots:
*/
void accountActivityChanged(bool active);
+ /**
+ * This is initially to run background account refresh tasks, or on a hourly timer
+ */
+ void fillQueue();
+
+private slots:
+ void tryNext();
+
+ void authSucceeded();
+ void authFailed(QString reason);
+
protected:
+ QList<QString> m_refreshQueue;
+ QTimer *m_refreshTimer;
+ QTimer *m_nextTimer;
+ shared_qobject_ptr<AccountTask> m_currentTask;
+
/*!
* Called whenever the list changes.
* This emits the listChanged() signal and autosaves the list (if autosave is enabled).
diff --git a/launcher/minecraft/auth/AccountTask.cpp b/launcher/minecraft/auth/AccountTask.cpp
index 25d753de..98d8d94d 100644
--- a/launcher/minecraft/auth/AccountTask.cpp
+++ b/launcher/minecraft/auth/AccountTask.cpp
@@ -28,40 +28,79 @@
AccountTask::AccountTask(AccountData *data, QObject *parent)
: Task(parent), m_data(data)
{
- changeState(STATE_CREATED);
+ changeState(AccountTaskState::STATE_CREATED);
}
QString AccountTask::getStateMessage() const
{
- switch (m_accountState)
+ switch (m_taskState)
{
- case STATE_CREATED:
+ case AccountTaskState::STATE_CREATED:
return "Waiting...";
- case STATE_WORKING:
+ case AccountTaskState::STATE_WORKING:
return tr("Sending request to auth servers...");
- case STATE_SUCCEEDED:
+ case AccountTaskState::STATE_SUCCEEDED:
return tr("Authentication task succeeded.");
- case STATE_FAILED_SOFT:
+ case AccountTaskState::STATE_OFFLINE:
return tr("Failed to contact the authentication server.");
- case STATE_FAILED_HARD:
- return tr("Failed to authenticate.");
- case STATE_FAILED_GONE:
+ case AccountTaskState::STATE_FAILED_SOFT:
+ return tr("Encountered an error during authentication.");
+ case AccountTaskState::STATE_FAILED_HARD:
+ return tr("Failed to authenticate. The session has expired.");
+ case AccountTaskState::STATE_FAILED_GONE:
return tr("Failed to authenticate. The account no longer exists.");
default:
return tr("...");
}
}
-void AccountTask::changeState(AccountTask::State newState, QString reason)
+bool AccountTask::changeState(AccountTaskState newState, QString reason)
{
- m_accountState = newState;
+ m_taskState = newState;
setStatus(getStateMessage());
- if (newState == STATE_SUCCEEDED)
- {
- emitSucceeded();
- }
- else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT || newState == STATE_FAILED_GONE)
- {
- emitFailed(reason);
+ switch(newState) {
+ case AccountTaskState::STATE_CREATED: {
+ m_data->errorString.clear();
+ return true;
+ }
+ case AccountTaskState::STATE_WORKING: {
+ m_data->accountState = AccountState::Working;
+ return true;
+ }
+ case AccountTaskState::STATE_SUCCEEDED: {
+ m_data->accountState = AccountState::Online;
+ emitSucceeded();
+ return false;
+ }
+ case AccountTaskState::STATE_OFFLINE: {
+ m_data->errorString = reason;
+ m_data->accountState = AccountState::Offline;
+ emitFailed(reason);
+ return false;
+ }
+ case AccountTaskState::STATE_FAILED_SOFT: {
+ m_data->errorString = reason;
+ m_data->accountState = AccountState::Errored;
+ emitFailed(reason);
+ return false;
+ }
+ case AccountTaskState::STATE_FAILED_HARD: {
+ m_data->errorString = reason;
+ m_data->accountState = AccountState::Expired;
+ emitFailed(reason);
+ return false;
+ }
+ case AccountTaskState::STATE_FAILED_GONE: {
+ m_data->errorString = reason;
+ m_data->accountState = AccountState::Gone;
+ emitFailed(reason);
+ return false;
+ }
+ default: {
+ QString error = tr("Unknown account task state: %1").arg(int(newState));
+ m_data->accountState = AccountState::Errored;
+ emitFailed(error);
+ return false;
+ }
}
}
diff --git a/launcher/minecraft/auth/AccountTask.h b/launcher/minecraft/auth/AccountTask.h
index 4f3bd52a..dac3f1b5 100644
--- a/launcher/minecraft/auth/AccountTask.h
+++ b/launcher/minecraft/auth/AccountTask.h
@@ -26,62 +26,32 @@
class QNetworkReply;
+/**
+ * Enum for describing the state of the current task.
+ * Used by the getStateMessage function to determine what the status message should be.
+ */
+enum class AccountTaskState
+{
+ STATE_CREATED,
+ STATE_WORKING,
+ STATE_SUCCEEDED,
+ STATE_FAILED_SOFT, //!< soft failure. authentication went through partially
+ STATE_FAILED_HARD, //!< hard failure. main tokens are invalid
+ STATE_FAILED_GONE, //!< hard failure. main tokens are invalid, and the account no longer exists
+ STATE_OFFLINE //!< soft failure. authentication failed in the first step in a 'soft' way
+};
+
class AccountTask : public Task
{
- friend class AuthContext;
Q_OBJECT
public:
explicit AccountTask(AccountData * data, QObject *parent = 0);
virtual ~AccountTask() {};
- /**
- * assign a session to this task. the session will be filled with required infomration
- * upon completion
- */
- void assignSession(AuthSessionPtr session)
- {
- m_session = session;
- }
-
- /// get the assigned session for filling with information.
- AuthSessionPtr getAssignedSession()
- {
- return m_session;
- }
-
- /**
- * Class describing a Account error response.
- */
- struct Error
- {
- QString m_errorMessageShort;
- QString m_errorMessageVerbose;
- QString m_cause;
- };
-
- enum AbortedBy
- {
- BY_NOTHING,
- BY_USER,
- BY_TIMEOUT
- } m_aborted = BY_NOTHING;
-
- /**
- * Enum for describing the state of the current task.
- * Used by the getStateMessage function to determine what the status message should be.
- */
- enum State
- {
- STATE_CREATED,
- STATE_WORKING,
- STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated
- STATE_FAILED_HARD, //!< hard failure. auth is invalid
- STATE_FAILED_GONE, //!< hard failure. auth is invalid, and the account no longer exists
- STATE_SUCCEEDED
- } m_accountState = STATE_CREATED;
+ AccountTaskState m_taskState = AccountTaskState::STATE_CREATED;
- State accountState() {
- return m_accountState;
+ AccountTaskState taskState() {
+ return m_taskState;
}
signals:
@@ -98,11 +68,9 @@ protected:
virtual QString getStateMessage() const;
protected slots:
- void changeState(State newState, QString reason=QString());
+ // NOTE: true -> non-terminal state, false -> terminal state
+ bool changeState(AccountTaskState newState, QString reason = QString());
protected:
- // FIXME: segfault disaster waiting to happen
AccountData *m_data = nullptr;
- std::shared_ptr<Error> m_error;
- AuthSessionPtr m_session;
};
diff --git a/launcher/minecraft/auth/flows/AuthRequest.cpp b/launcher/minecraft/auth/AuthRequest.cpp
index 82dba591..459d2354 100644
--- a/launcher/minecraft/auth/flows/AuthRequest.cpp
+++ b/launcher/minecraft/auth/AuthRequest.cpp
@@ -44,6 +44,7 @@ void AuthRequest::onRequestFinished() {
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
+ httpStatus_ = 200;
finish();
}
@@ -55,10 +56,11 @@ void AuthRequest::onRequestError(QNetworkReply::NetworkError error) {
if (reply_ != qobject_cast<QNetworkReply *>(sender())) {
return;
}
- qWarning() << "AuthRequest::onRequestError: Error string: " << reply_->errorString();
- int httpStatus = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
- qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
+ errorString_ = reply_->errorString();
+ httpStatus_ = reply_->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
error_ = error;
+ qWarning() << "AuthRequest::onRequestError: Error string: " << errorString_;
+ qWarning() << "AuthRequest::onRequestError: HTTP status" << httpStatus_ << reply_->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString();
// QTimer::singleShot(10, this, SLOT(finish()));
}
@@ -103,6 +105,8 @@ void AuthRequest::setup(const QNetworkRequest &req, QNetworkAccessManager::Opera
status_ = Requesting;
error_ = QNetworkReply::NoError;
+ errorString_.clear();
+ httpStatus_ = 0;
}
void AuthRequest::finish() {
diff --git a/launcher/minecraft/auth/flows/AuthRequest.h b/launcher/minecraft/auth/AuthRequest.h
index a547aea4..89f7a123 100644
--- a/launcher/minecraft/auth/flows/AuthRequest.h
+++ b/launcher/minecraft/auth/AuthRequest.h
@@ -46,6 +46,11 @@ protected slots:
/// Handle upload progress.
void onUploadProgress(qint64 uploaded, qint64 total);
+public:
+ QNetworkReply::NetworkError error_;
+ int httpStatus_ = 0;
+ QString errorString_;
+
protected: