GH-4071 Heavily refactor and rearchitect account system

This makes the account system much more modular
and makes it treat errors as something recoverable,
unless they come directly from the MSA refresh token
becoming invalid.
This commit is contained in:
Petr Mrázek
2021-12-04 01:18:05 +01:00
parent ffcef673de
commit 3c46d8a412
68 changed files with 2105 additions and 1446 deletions

View File

@ -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");
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");
}
}
if(account->isExpired()) {
return tr("Expired", "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);
}
}