Offline mode support, part 1
Refactor MojangAccount so it exposes a less generic interface and supports login. Hide the ugly details. Yggdrasil tasks are now only used from MojangAccount.
This commit is contained in:
parent
613699b362
commit
f028aa76bc
@ -69,10 +69,6 @@
|
||||
#include "logic/lists/IconList.h"
|
||||
#include "logic/lists/JavaVersionList.h"
|
||||
|
||||
#include "logic/auth/flows/AuthenticateTask.h"
|
||||
#include "logic/auth/flows/RefreshTask.h"
|
||||
#include "logic/auth/flows/ValidateTask.h"
|
||||
|
||||
#include "logic/BaseInstance.h"
|
||||
#include "logic/InstanceFactory.h"
|
||||
#include "logic/MinecraftProcess.h"
|
||||
@ -210,9 +206,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
|
||||
|
||||
for(AccountProfile profile : account->profiles())
|
||||
{
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png");
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
|
||||
auto action = CacheDownload::make(
|
||||
QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"),
|
||||
QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name + ".png"),
|
||||
meta);
|
||||
job->addNetAction(action);
|
||||
meta->stale = true;
|
||||
@ -310,9 +306,9 @@ void MainWindow::repopulateAccountsMenu()
|
||||
section->setEnabled(false);
|
||||
accountMenu->addAction(section);
|
||||
|
||||
for (AccountProfile profile : account->profiles())
|
||||
for (auto profile : account->profiles())
|
||||
{
|
||||
QAction *action = new QAction(profile.name(), this);
|
||||
QAction *action = new QAction(profile.name, this);
|
||||
action->setData(account->username());
|
||||
action->setCheckable(true);
|
||||
if(active_username == account->username())
|
||||
@ -320,7 +316,7 @@ void MainWindow::repopulateAccountsMenu()
|
||||
action->setChecked(true);
|
||||
}
|
||||
|
||||
action->setIcon(SkinUtils::getFaceFromCache(profile.name()));
|
||||
action->setIcon(SkinUtils::getFaceFromCache(profile.name));
|
||||
accountMenu->addAction(action);
|
||||
connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount()));
|
||||
}
|
||||
@ -378,7 +374,7 @@ void MainWindow::activeAccountChanged()
|
||||
const AccountProfile *profile = account->currentProfile();
|
||||
if (profile != nullptr)
|
||||
{
|
||||
accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name()));
|
||||
accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -790,6 +786,7 @@ void MainWindow::doLaunch()
|
||||
void MainWindow::doLaunchInst(BaseInstance* instance, MojangAccountPtr account)
|
||||
{
|
||||
// We'll need to validate the access token to make sure the account is still logged in.
|
||||
/*
|
||||
ProgressDialog progDialog(this);
|
||||
RefreshTask refreshtask(account, &progDialog);
|
||||
progDialog.exec(&refreshtask);
|
||||
@ -829,10 +826,12 @@ void MainWindow::doLaunchInst(BaseInstance* instance, MojangAccountPtr account)
|
||||
QMessageBox::Warning, QMessageBox::Ok)->exec();
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
bool MainWindow::doRefreshToken(MojangAccountPtr account, const QString& errorMsg)
|
||||
{
|
||||
/*
|
||||
EditAccountDialog passDialog(errorMsg, this, EditAccountDialog::PasswordField);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
@ -848,7 +847,8 @@ bool MainWindow::doRefreshToken(MojangAccountPtr account, const QString& errorMs
|
||||
return doRefreshToken(account, authTask.failReason());
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
else return false;*/
|
||||
return false;
|
||||
}
|
||||
|
||||
void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account)
|
||||
|
@ -20,7 +20,6 @@
|
||||
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
#include <logic/auth/flows/AuthenticateTask.h>
|
||||
#include <logic/net/NetJob.h>
|
||||
|
||||
#include <gui/dialogs/EditAccountDialog.h>
|
||||
@ -117,8 +116,8 @@ void AccountListDialog::addAccount(const QString& errMsg)
|
||||
QString username(loginDialog.username());
|
||||
QString password(loginDialog.password());
|
||||
|
||||
MojangAccountPtr account = MojangAccountPtr(new MojangAccount(username));
|
||||
|
||||
MojangAccountPtr account = MojangAccount::createFromUsername(username);
|
||||
/*
|
||||
ProgressDialog progDialog(this);
|
||||
AuthenticateTask authTask(account, password, &progDialog);
|
||||
if (progDialog.exec(&authTask))
|
||||
@ -132,9 +131,9 @@ void AccountListDialog::addAccount(const QString& errMsg)
|
||||
|
||||
for(AccountProfile profile : account->profiles())
|
||||
{
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png");
|
||||
auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png");
|
||||
auto action = CacheDownload::make(
|
||||
QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"),
|
||||
QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name + ".png"),
|
||||
meta);
|
||||
job->addNetAction(action);
|
||||
meta->stale = true;
|
||||
@ -142,5 +141,6 @@ void AccountListDialog::addAccount(const QString& errMsg)
|
||||
|
||||
job->start();
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,6 @@
|
||||
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
#include <logic/auth/flows/AuthenticateTask.h>
|
||||
|
||||
#include <gui/dialogs/ProgressDialog.h>
|
||||
|
||||
#include <MultiMC.h>
|
||||
|
@ -105,7 +105,7 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account)
|
||||
#endif
|
||||
|
||||
args << "-jar" << LAUNCHER_FILE;
|
||||
args << account->currentProfile()->name();
|
||||
args << account->currentProfile()->name;
|
||||
args << account->sessionId();
|
||||
args << windowTitle;
|
||||
args << windowSize;
|
||||
|
@ -75,20 +75,22 @@ QString MinecraftProcess::censorPrivateInfo(QString in)
|
||||
{
|
||||
if(!m_account)
|
||||
return in;
|
||||
else
|
||||
{
|
||||
|
||||
QString sessionId = m_account->sessionId();
|
||||
QString accessToken = m_account->accessToken();
|
||||
QString clientToken = m_account->clientToken();
|
||||
QString profileId = m_account->currentProfile()->id();
|
||||
QString profileName = m_account->currentProfile()->name();
|
||||
in.replace(sessionId, "<SESSION ID>");
|
||||
in.replace(accessToken, "<ACCESS TOKEN>");
|
||||
in.replace(clientToken, "<CLIENT TOKEN>");
|
||||
auto profile = m_account->currentProfile();
|
||||
if(profile)
|
||||
{
|
||||
QString profileId = profile->id;
|
||||
QString profileName = profile->name;
|
||||
in.replace(profileId, "<PROFILE ID>");
|
||||
in.replace(profileName, "<PROFILE NAME>");
|
||||
return in;
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
// console window
|
||||
|
@ -77,8 +77,8 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account)
|
||||
token_mapping["auth_username"] = account->username();
|
||||
token_mapping["auth_session"] = account->sessionId();
|
||||
token_mapping["auth_access_token"] = account->accessToken();
|
||||
token_mapping["auth_player_name"] = account->currentProfile()->name();
|
||||
token_mapping["auth_uuid"] = account->currentProfile()->id();
|
||||
token_mapping["auth_player_name"] = account->currentProfile()->name;
|
||||
token_mapping["auth_uuid"] = account->currentProfile()->id;
|
||||
|
||||
// this is for offline?:
|
||||
/*
|
||||
|
@ -16,113 +16,16 @@
|
||||
*/
|
||||
|
||||
#include "MojangAccount.h"
|
||||
#include "flows/RefreshTask.h"
|
||||
#include "flows/AuthenticateTask.h"
|
||||
|
||||
#include <QUuid>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QRegExp>
|
||||
|
||||
#include <logger/QsLog.h>
|
||||
|
||||
MojangAccount::MojangAccount(const QString &username, QObject *parent) : QObject(parent)
|
||||
{
|
||||
// Generate a client token.
|
||||
m_clientToken = QUuid::createUuid().toString();
|
||||
|
||||
m_username = username;
|
||||
|
||||
m_currentProfile = -1;
|
||||
}
|
||||
|
||||
MojangAccount::MojangAccount(const QString &username, const QString &clientToken,
|
||||
const QString &accessToken, QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_username = username;
|
||||
m_clientToken = clientToken;
|
||||
m_accessToken = accessToken;
|
||||
|
||||
m_currentProfile = -1;
|
||||
}
|
||||
|
||||
MojangAccount::MojangAccount(const MojangAccount &other, QObject *parent)
|
||||
{
|
||||
m_username = other.username();
|
||||
m_clientToken = other.clientToken();
|
||||
m_accessToken = other.accessToken();
|
||||
|
||||
m_profiles = other.m_profiles;
|
||||
m_currentProfile = other.m_currentProfile;
|
||||
}
|
||||
|
||||
QString MojangAccount::username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
QString MojangAccount::clientToken() const
|
||||
{
|
||||
return m_clientToken;
|
||||
}
|
||||
|
||||
void MojangAccount::setClientToken(const QString &clientToken)
|
||||
{
|
||||
m_clientToken = clientToken;
|
||||
}
|
||||
|
||||
QString MojangAccount::accessToken() const
|
||||
{
|
||||
return m_accessToken;
|
||||
}
|
||||
|
||||
void MojangAccount::setAccessToken(const QString &accessToken)
|
||||
{
|
||||
m_accessToken = accessToken;
|
||||
}
|
||||
|
||||
QString MojangAccount::sessionId() const
|
||||
{
|
||||
return "token:" + m_accessToken + ":" + currentProfile()->id();
|
||||
}
|
||||
|
||||
const QList<AccountProfile> MojangAccount::profiles() const
|
||||
{
|
||||
return m_profiles;
|
||||
}
|
||||
|
||||
const AccountProfile *MojangAccount::currentProfile() const
|
||||
{
|
||||
if (m_currentProfile < 0)
|
||||
{
|
||||
if (m_profiles.length() > 0)
|
||||
return &m_profiles.at(0);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
return &m_profiles.at(m_currentProfile);
|
||||
}
|
||||
|
||||
bool MojangAccount::setProfile(const QString &profileId)
|
||||
{
|
||||
const QList<AccountProfile> &profiles = this->profiles();
|
||||
for (int i = 0; i < profiles.length(); i++)
|
||||
{
|
||||
if (profiles.at(i).id() == profileId)
|
||||
{
|
||||
m_currentProfile = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MojangAccount::loadProfiles(const ProfileList &profiles)
|
||||
{
|
||||
m_profiles.clear();
|
||||
for (auto profile : profiles)
|
||||
m_profiles.append(profile);
|
||||
}
|
||||
|
||||
MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
||||
{
|
||||
// The JSON object must at least have a username for it to be valid.
|
||||
@ -143,7 +46,7 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ProfileList profiles;
|
||||
QList<AccountProfile> profiles;
|
||||
for (QJsonValue profileVal : profileArray)
|
||||
{
|
||||
QJsonObject profileObject = profileVal.toObject();
|
||||
@ -154,67 +57,112 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object)
|
||||
QLOG_WARN() << "Unable to load a profile because it was missing an ID or a name.";
|
||||
continue;
|
||||
}
|
||||
profiles.append(AccountProfile(id, name));
|
||||
profiles.append({id, name});
|
||||
}
|
||||
|
||||
MojangAccountPtr account(new MojangAccount(username, clientToken, accessToken));
|
||||
account->loadProfiles(profiles);
|
||||
MojangAccountPtr account(new MojangAccount());
|
||||
account->m_username = username;
|
||||
account->m_clientToken = clientToken;
|
||||
account->m_accessToken = accessToken;
|
||||
account->m_profiles = profiles;
|
||||
|
||||
// Get the currently selected profile.
|
||||
QString currentProfile = object.value("activeProfile").toString("");
|
||||
if (!currentProfile.isEmpty())
|
||||
account->setProfile(currentProfile);
|
||||
account->setCurrentProfile(currentProfile);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
QJsonObject MojangAccount::saveToJson()
|
||||
MojangAccountPtr MojangAccount::createFromUsername(const QString& username)
|
||||
{
|
||||
MojangAccountPtr account(new MojangAccount());
|
||||
account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
|
||||
account->m_username = username;
|
||||
return account;
|
||||
}
|
||||
|
||||
QJsonObject MojangAccount::saveToJson() const
|
||||
{
|
||||
QJsonObject json;
|
||||
json.insert("username", username());
|
||||
json.insert("clientToken", clientToken());
|
||||
json.insert("accessToken", accessToken());
|
||||
json.insert("username", m_username);
|
||||
json.insert("clientToken", m_clientToken);
|
||||
json.insert("accessToken", m_accessToken);
|
||||
|
||||
QJsonArray profileArray;
|
||||
for (AccountProfile profile : m_profiles)
|
||||
{
|
||||
QJsonObject profileObj;
|
||||
profileObj.insert("id", profile.id());
|
||||
profileObj.insert("name", profile.name());
|
||||
profileObj.insert("id", profile.id);
|
||||
profileObj.insert("name", profile.name);
|
||||
profileArray.append(profileObj);
|
||||
}
|
||||
json.insert("profiles", profileArray);
|
||||
|
||||
if (currentProfile() != nullptr)
|
||||
json.insert("activeProfile", currentProfile()->id());
|
||||
if (m_currentProfile != -1)
|
||||
json.insert("activeProfile", currentProfile()->id);
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
|
||||
AccountProfile::AccountProfile(const QString& id, const QString& name)
|
||||
bool MojangAccount::setCurrentProfile(const QString &profileId)
|
||||
{
|
||||
m_id = id;
|
||||
m_name = name;
|
||||
for (int i = 0; i < m_profiles.length(); i++)
|
||||
{
|
||||
if (m_profiles[i].id == profileId)
|
||||
{
|
||||
m_currentProfile = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AccountProfile::AccountProfile(const AccountProfile &other)
|
||||
const AccountProfile* MojangAccount::currentProfile() const
|
||||
{
|
||||
m_id = other.m_id;
|
||||
m_name = other.m_name;
|
||||
if(m_currentProfile == -1)
|
||||
return nullptr;
|
||||
return &m_profiles[m_currentProfile];
|
||||
}
|
||||
|
||||
QString AccountProfile::id() const
|
||||
AccountStatus MojangAccount::accountStatus() const
|
||||
{
|
||||
return m_id;
|
||||
if(m_accessToken.isEmpty())
|
||||
return NotVerified;
|
||||
if(!m_online)
|
||||
return Verified;
|
||||
return Online;
|
||||
}
|
||||
|
||||
QString AccountProfile::name() const
|
||||
bool MojangAccount::login(QString password)
|
||||
{
|
||||
return m_name;
|
||||
if(m_currentTask)
|
||||
return false;
|
||||
if(password.isEmpty())
|
||||
{
|
||||
m_currentTask.reset(new RefreshTask(this, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentTask.reset(new AuthenticateTask(this, password, this));
|
||||
}
|
||||
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
|
||||
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
|
||||
m_currentTask->start();
|
||||
return true;
|
||||
}
|
||||
|
||||
void MojangAccount::propagateChange()
|
||||
void MojangAccount::authSucceeded()
|
||||
{
|
||||
m_online = true;
|
||||
m_currentTask.reset();
|
||||
emit changed();
|
||||
}
|
||||
|
||||
void MojangAccount::authFailed(QString reason)
|
||||
{
|
||||
m_online = false;
|
||||
m_accessToken = QString();
|
||||
m_currentTask.reset();
|
||||
emit changed();
|
||||
}
|
||||
|
@ -23,34 +23,25 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
class YggdrasilTask;
|
||||
class MojangAccount;
|
||||
|
||||
typedef std::shared_ptr<MojangAccount> MojangAccountPtr;
|
||||
Q_DECLARE_METATYPE(MojangAccountPtr)
|
||||
|
||||
/**
|
||||
* Class that represents a profile within someone's Mojang account.
|
||||
* A profile within someone's Mojang account.
|
||||
*
|
||||
* Currently, the profile system has not been implemented by Mojang yet,
|
||||
* but we might as well add some things for it in MultiMC right now so
|
||||
* we don't have to rip the code to pieces to add it later.
|
||||
*/
|
||||
class AccountProfile
|
||||
struct AccountProfile
|
||||
{
|
||||
public:
|
||||
AccountProfile(const QString &id, const QString &name);
|
||||
AccountProfile(const AccountProfile &other);
|
||||
|
||||
QString id() const;
|
||||
QString name() const;
|
||||
|
||||
protected:
|
||||
QString m_id;
|
||||
QString m_name;
|
||||
QString id;
|
||||
QString name;
|
||||
};
|
||||
|
||||
typedef QList<AccountProfile> ProfileList;
|
||||
|
||||
struct User
|
||||
{
|
||||
QString id;
|
||||
@ -59,6 +50,13 @@ struct User
|
||||
QList<QPair<QString, QString>> properties;
|
||||
};
|
||||
|
||||
enum AccountStatus
|
||||
{
|
||||
NotVerified,
|
||||
Verified,
|
||||
Online
|
||||
};
|
||||
|
||||
/**
|
||||
* Object that stores information about a certain Mojang account.
|
||||
*
|
||||
@ -68,106 +66,112 @@ struct User
|
||||
class MojangAccount : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Constructs a new MojangAccount with the given username.
|
||||
* The client token will be generated automatically and the access token will be blank.
|
||||
*/
|
||||
explicit MojangAccount(const QString &username, QObject *parent = 0);
|
||||
public: /* construction */
|
||||
//! Do not copy accounts. ever.
|
||||
explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete;
|
||||
|
||||
/**
|
||||
* Constructs a new MojangAccount with the given username, client token, and access token.
|
||||
*/
|
||||
explicit MojangAccount(const QString &username, const QString &clientToken,
|
||||
const QString &accessToken, QObject *parent = 0);
|
||||
//! Default constructor
|
||||
explicit MojangAccount(QObject *parent = 0) : QObject(parent) {};
|
||||
|
||||
/**
|
||||
* Constructs a new MojangAccount matching the given account.
|
||||
*/
|
||||
MojangAccount(const MojangAccount &other, QObject *parent);
|
||||
//! Creates an empty account for the specified user name.
|
||||
static MojangAccountPtr createFromUsername(const QString &username);
|
||||
|
||||
/**
|
||||
* Loads a MojangAccount from the given JSON object.
|
||||
*/
|
||||
//! Loads a MojangAccount from the given JSON object.
|
||||
static MojangAccountPtr loadFromJson(const QJsonObject &json);
|
||||
|
||||
/**
|
||||
* Saves a MojangAccount to a JSON object and returns it.
|
||||
*/
|
||||
QJsonObject saveToJson();
|
||||
|
||||
/**
|
||||
* Update the account on disk and lists (it changed, for whatever reason)
|
||||
* This is called by various Yggdrasil tasks.
|
||||
*/
|
||||
void propagateChange();
|
||||
|
||||
/**
|
||||
* This MojangAccount's username. May be an email address if the account is migrated.
|
||||
*/
|
||||
QString username() const;
|
||||
|
||||
/**
|
||||
* This MojangAccount's client token. This is a UUID used by Mojang's auth servers to identify this client.
|
||||
* This is unique for each MojangAccount.
|
||||
*/
|
||||
QString clientToken() const;
|
||||
|
||||
/**
|
||||
* Sets the MojangAccount's client token to the given value.
|
||||
*/
|
||||
void setClientToken(const QString &token);
|
||||
|
||||
/**
|
||||
* This MojangAccount's access token.
|
||||
* If the user has not chosen to stay logged in, this will be an empty string.
|
||||
*/
|
||||
QString accessToken() const;
|
||||
|
||||
/**
|
||||
* Changes this MojangAccount's access token to the given value.
|
||||
*/
|
||||
void setAccessToken(const QString &token);
|
||||
|
||||
/**
|
||||
* Get full session ID
|
||||
*/
|
||||
QString sessionId() const;
|
||||
|
||||
/**
|
||||
* Returns a list of the available account profiles.
|
||||
*/
|
||||
const ProfileList profiles() const;
|
||||
|
||||
/**
|
||||
* Returns a pointer to the currently selected profile.
|
||||
* If no profile is selected, returns the first profile in the profile list or nullptr if there are none.
|
||||
*/
|
||||
const AccountProfile *currentProfile() const;
|
||||
//! Saves a MojangAccount to a JSON object and returns it.
|
||||
QJsonObject saveToJson() const;
|
||||
|
||||
public: /* manipulation */
|
||||
/**
|
||||
* Sets the currently selected profile to the profile with the given ID string.
|
||||
* If profileId is not in the list of available profiles, the function will simply return false.
|
||||
* If profileId is not in the list of available profiles, the function will simply return
|
||||
* false.
|
||||
*/
|
||||
bool setProfile(const QString &profileId);
|
||||
bool setCurrentProfile(const QString &profileId);
|
||||
|
||||
/**
|
||||
* Clears the current account profile list and replaces it with the given profile list.
|
||||
* Attempt to login. Empty password means we use the token.
|
||||
* If the attempt fails because we already are performing some task, it returns false.
|
||||
*/
|
||||
void loadProfiles(const ProfileList &profiles);
|
||||
bool login(QString password = QString());
|
||||
|
||||
public: /* queries */
|
||||
const QString &username() const
|
||||
{
|
||||
return m_username;
|
||||
}
|
||||
|
||||
const QString &clientToken() const
|
||||
{
|
||||
return m_clientToken;
|
||||
}
|
||||
|
||||
const QString &accessToken() const
|
||||
{
|
||||
return m_accessToken;
|
||||
}
|
||||
|
||||
const QList<AccountProfile> &profiles() const
|
||||
{
|
||||
return m_profiles;
|
||||
}
|
||||
|
||||
//! Get the session ID required for legacy Minecraft versions
|
||||
QString sessionId() const
|
||||
{
|
||||
if (m_currentProfile != -1 && !m_accessToken.isEmpty())
|
||||
return "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id;
|
||||
return "-";
|
||||
}
|
||||
|
||||
//! Returns the currently selected profile (if none, returns nullptr)
|
||||
const AccountProfile *currentProfile() const;
|
||||
|
||||
//! Returns whether the account is NotVerified, Verified or Online
|
||||
AccountStatus accountStatus() const;
|
||||
|
||||
signals:
|
||||
/**
|
||||
* This isgnal is emitted whrn the account changes
|
||||
* This signal is emitted when the account changes
|
||||
*/
|
||||
void changed();
|
||||
|
||||
protected:
|
||||
// TODO: better signalling for the various possible state changes - especially errors
|
||||
|
||||
protected: /* variables */
|
||||
QString m_username;
|
||||
|
||||
// Used to identify the client - the user can have multiple clients for the same account
|
||||
// Think: different launchers, all connecting to the same account/profile
|
||||
QString m_clientToken;
|
||||
QString m_accessToken; // Blank if not logged in.
|
||||
int m_currentProfile; // Index of the selected profile within the list of available
|
||||
|
||||
// Blank if not logged in.
|
||||
QString m_accessToken;
|
||||
|
||||
// Index of the selected profile within the list of available
|
||||
// profiles. -1 if nothing is selected.
|
||||
ProfileList m_profiles; // List of available profiles.
|
||||
User m_user; // the user structure, whatever it is.
|
||||
int m_currentProfile = -1;
|
||||
|
||||
// List of available profiles.
|
||||
QList<AccountProfile> m_profiles;
|
||||
|
||||
// the user structure, whatever it is.
|
||||
User m_user;
|
||||
|
||||
// true when the account is verified
|
||||
bool m_online = false;
|
||||
|
||||
// current task we are executing here
|
||||
std::shared_ptr<YggdrasilTask> m_currentTask;
|
||||
|
||||
private slots:
|
||||
void authSucceeded();
|
||||
void authFailed(QString reason);
|
||||
|
||||
public:
|
||||
friend class YggdrasilTask;
|
||||
friend class AuthenticateTask;
|
||||
friend class ValidateTask;
|
||||
friend class RefreshTask;
|
||||
};
|
||||
|
@ -25,10 +25,9 @@
|
||||
#include <MultiMC.h>
|
||||
#include <logic/auth/MojangAccount.h>
|
||||
|
||||
YggdrasilTask::YggdrasilTask(MojangAccountPtr account, QObject *parent) : Task(parent)
|
||||
YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent)
|
||||
: Task(parent), m_account(account)
|
||||
{
|
||||
m_error = nullptr;
|
||||
m_account = account;
|
||||
}
|
||||
|
||||
YggdrasilTask::~YggdrasilTask()
|
||||
@ -81,7 +80,8 @@ void YggdrasilTask::processReply(QNetworkReply *reply)
|
||||
|
||||
if (responseCode == 200)
|
||||
{
|
||||
// If the response code was 200, then there shouldn't be an error. Make sure anyways.
|
||||
// If the response code was 200, then there shouldn't be an error. Make sure
|
||||
// anyways.
|
||||
// Also, sometimes an empty reply indicates success. If there was no data received,
|
||||
// pass an empty json object to the processResponse function.
|
||||
if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0)
|
||||
@ -102,25 +102,34 @@ void YggdrasilTask::processReply(QNetworkReply *reply)
|
||||
}
|
||||
else
|
||||
{
|
||||
emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset));
|
||||
emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.")
|
||||
.arg(jsonError.errorString())
|
||||
.arg(jsonError.offset));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the response code was not 200, then Yggdrasil may have given us information about the error.
|
||||
// If we can parse the response, then get information from it. Otherwise just say there was an unknown error.
|
||||
// If the response code was not 200, then Yggdrasil may have given us information
|
||||
// about the error.
|
||||
// If we can parse the response, then get information from it. Otherwise just say
|
||||
// there was an unknown error.
|
||||
if (jsonError.error == QJsonParseError::NoError)
|
||||
{
|
||||
// We were able to parse the server's response. Woo!
|
||||
// Call processError. If a subclass has overridden it then they'll handle their stuff there.
|
||||
QLOG_DEBUG() << "The request failed, but the server gave us an error message. Processing error.";
|
||||
// Call processError. If a subclass has overridden it then they'll handle their
|
||||
// stuff there.
|
||||
QLOG_DEBUG() << "The request failed, but the server gave us an error message. "
|
||||
"Processing error.";
|
||||
emitFailed(processError(doc.object()));
|
||||
}
|
||||
else
|
||||
{
|
||||
// The server didn't say anything regarding the error. Give the user an unknown error.
|
||||
QLOG_DEBUG() << "The request failed and the server gave no error message. Unknown error.";
|
||||
emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(reply->errorString()));
|
||||
// The server didn't say anything regarding the error. Give the user an unknown
|
||||
// error.
|
||||
QLOG_DEBUG() << "The request failed and the server gave no error message. "
|
||||
"Unknown error.";
|
||||
emitFailed(tr("An unknown error occurred when trying to communicate with the "
|
||||
"authentication server: %1").arg(reply->errorString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,8 +170,3 @@ YggdrasilTask::Error *YggdrasilTask::getError() const
|
||||
{
|
||||
return this->m_error;
|
||||
}
|
||||
|
||||
MojangAccountPtr YggdrasilTask::getMojangAccount() const
|
||||
{
|
||||
return this->m_account;
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ class YggdrasilTask : public Task
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit YggdrasilTask(MojangAccountPtr account, QObject *parent = 0);
|
||||
explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0);
|
||||
~YggdrasilTask();
|
||||
|
||||
/**
|
||||
@ -59,11 +59,6 @@ public:
|
||||
QString m_cause;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the Mojang account that this task is operating on.
|
||||
*/
|
||||
virtual MojangAccountPtr getMojangAccount() const;
|
||||
|
||||
/**
|
||||
* Returns a pointer to a YggdrasilTask::Error object if an error has occurred.
|
||||
* If no error has occurred, returns a null pointer.
|
||||
@ -120,11 +115,11 @@ protected:
|
||||
*/
|
||||
virtual QString getStateMessage(const State state) const;
|
||||
|
||||
MojangAccountPtr m_account;
|
||||
MojangAccount *m_account = nullptr;
|
||||
|
||||
QNetworkReply *m_netReply;
|
||||
|
||||
Error *m_error;
|
||||
Error *m_error = nullptr;
|
||||
|
||||
protected
|
||||
slots:
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
AuthenticateTask::AuthenticateTask(MojangAccountPtr account, const QString &password,
|
||||
AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password,
|
||||
QObject *parent)
|
||||
: YggdrasilTask(account, parent), m_password(password)
|
||||
{
|
||||
@ -59,14 +59,14 @@ QJsonObject AuthenticateTask::getRequestContent() const
|
||||
req.insert("agent", agent);
|
||||
}
|
||||
|
||||
req.insert("username", getMojangAccount()->username());
|
||||
req.insert("username", m_account->username());
|
||||
req.insert("password", m_password);
|
||||
req.insert("requestUser", true);
|
||||
|
||||
// If we already have a client token, give it to the server.
|
||||
// Otherwise, let the server give us one.
|
||||
if (!getMojangAccount()->clientToken().isEmpty())
|
||||
req.insert("clientToken", getMojangAccount()->clientToken());
|
||||
if (!m_account->m_clientToken.isEmpty())
|
||||
req.insert("clientToken", m_account->m_clientToken);
|
||||
|
||||
return req;
|
||||
}
|
||||
@ -88,8 +88,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
QLOG_ERROR() << "Server didn't send a client token.";
|
||||
return false;
|
||||
}
|
||||
if (!getMojangAccount()->clientToken().isEmpty() &&
|
||||
clientToken != getMojangAccount()->clientToken())
|
||||
if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
|
||||
{
|
||||
// The server changed our client token! Obey its wishes, but complain. That's what I do
|
||||
// for my parents, so...
|
||||
@ -97,7 +96,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
<< "'. This shouldn't happen, but it isn't really a big deal.";
|
||||
}
|
||||
// Set the client token.
|
||||
getMojangAccount()->setClientToken(clientToken);
|
||||
m_account->m_clientToken = clientToken;
|
||||
|
||||
// Now, we set the access token.
|
||||
QLOG_DEBUG() << "Getting access token.";
|
||||
@ -109,7 +108,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
QLOG_ERROR() << "Server didn't send an access token.";
|
||||
}
|
||||
// Set the access token.
|
||||
getMojangAccount()->setAccessToken(accessToken);
|
||||
m_account->m_accessToken = accessToken;
|
||||
|
||||
// Now we load the list of available profiles.
|
||||
// Mojang hasn't yet implemented the profile system,
|
||||
@ -117,7 +116,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
// don't have trouble implementing it later.
|
||||
QLOG_DEBUG() << "Loading profile list.";
|
||||
QJsonArray availableProfiles = responseData.value("availableProfiles").toArray();
|
||||
ProfileList loadedProfiles;
|
||||
QList<AccountProfile> loadedProfiles;
|
||||
for (auto iter : availableProfiles)
|
||||
{
|
||||
QJsonObject profile = iter.toObject();
|
||||
@ -135,10 +134,10 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
}
|
||||
|
||||
// Now, add a new AccountProfile entry to the list.
|
||||
loadedProfiles.append(AccountProfile(id, name));
|
||||
loadedProfiles.append({id, name});
|
||||
}
|
||||
// Put the list of profiles we loaded into the MojangAccount object.
|
||||
getMojangAccount()->loadProfiles(loadedProfiles);
|
||||
m_account->m_profiles = loadedProfiles;
|
||||
|
||||
// Finally, we set the current profile to the correct value. This is pretty simple.
|
||||
// We do need to make sure that the current profile that the server gave us
|
||||
@ -153,7 +152,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData)
|
||||
QLOG_ERROR() << "Server didn't specify a currently selected profile.";
|
||||
return false;
|
||||
}
|
||||
if (!getMojangAccount()->setProfile(currentProfileId))
|
||||
if (!m_account->setCurrentProfile(currentProfileId))
|
||||
{
|
||||
// TODO: Set an error to display to the user.
|
||||
QLOG_ERROR() << "Server specified a selected profile that wasn't in the available "
|
||||
|
@ -30,7 +30,7 @@ class AuthenticateTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
AuthenticateTask(MojangAccountPtr account, const QString &password, QObject *parent = 0);
|
||||
AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
virtual QJsonObject getRequestContent() const;
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
RefreshTask::RefreshTask(MojangAccountPtr account, QObject *parent)
|
||||
RefreshTask::RefreshTask(MojangAccount *account, QObject *parent)
|
||||
: YggdrasilTask(account, parent)
|
||||
{
|
||||
}
|
||||
@ -44,13 +44,12 @@ QJsonObject RefreshTask::getRequestContent() const
|
||||
* "requestUser": true/false // request the user structure
|
||||
* }
|
||||
*/
|
||||
auto account = getMojangAccount();
|
||||
QJsonObject req;
|
||||
req.insert("clientToken", account->clientToken());
|
||||
req.insert("accessToken", account->accessToken());
|
||||
req.insert("clientToken", m_account->m_clientToken);
|
||||
req.insert("accessToken", m_account->m_accessToken);
|
||||
/*
|
||||
{
|
||||
auto currentProfile = account->currentProfile();
|
||||
auto currentProfile = m_account->currentProfile();
|
||||
QJsonObject profile;
|
||||
profile.insert("id", currentProfile->id());
|
||||
profile.insert("name", currentProfile->name());
|
||||
@ -64,8 +63,6 @@ QJsonObject RefreshTask::getRequestContent() const
|
||||
|
||||
bool RefreshTask::processResponse(QJsonObject responseData)
|
||||
{
|
||||
auto account = getMojangAccount();
|
||||
|
||||
// Read the response data. We need to get the client token, access token, and the selected
|
||||
// profile.
|
||||
QLOG_DEBUG() << "Processing authentication response.";
|
||||
@ -80,7 +77,7 @@ bool RefreshTask::processResponse(QJsonObject responseData)
|
||||
QLOG_ERROR() << "Server didn't send a client token.";
|
||||
return false;
|
||||
}
|
||||
if (!account->clientToken().isEmpty() && clientToken != account->clientToken())
|
||||
if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken)
|
||||
{
|
||||
// The server changed our client token! Obey its wishes, but complain. That's what I do
|
||||
// for my parents, so...
|
||||
@ -104,7 +101,7 @@ bool RefreshTask::processResponse(QJsonObject responseData)
|
||||
// profile)
|
||||
QJsonObject currentProfile = responseData.value("selectedProfile").toObject();
|
||||
QString currentProfileId = currentProfile.value("id").toString("");
|
||||
if (account->currentProfile()->id() != currentProfileId)
|
||||
if (m_account->currentProfile()->id != currentProfileId)
|
||||
{
|
||||
// TODO: Set an error to display to the user.
|
||||
QLOG_ERROR() << "Server didn't specify the same selected profile as ours.";
|
||||
@ -132,8 +129,7 @@ bool RefreshTask::processResponse(QJsonObject responseData)
|
||||
// we've succeeded.
|
||||
QLOG_DEBUG() << "Finished reading refresh response.";
|
||||
// Reset the access token.
|
||||
account->setAccessToken(accessToken);
|
||||
account->propagateChange();
|
||||
m_account->m_accessToken = accessToken;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ class RefreshTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
RefreshTask(MojangAccountPtr account, QObject *parent = 0);
|
||||
RefreshTask(MojangAccount * account, QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
virtual QJsonObject getRequestContent() const;
|
||||
|
@ -26,7 +26,7 @@
|
||||
|
||||
#include "logger/QsLog.h"
|
||||
|
||||
ValidateTask::ValidateTask(MojangAccountPtr account, QObject *parent)
|
||||
ValidateTask::ValidateTask(MojangAccount * account, QObject *parent)
|
||||
: YggdrasilTask(account, parent)
|
||||
{
|
||||
}
|
||||
@ -34,7 +34,7 @@ ValidateTask::ValidateTask(MojangAccountPtr account, QObject *parent)
|
||||
QJsonObject ValidateTask::getRequestContent() const
|
||||
{
|
||||
QJsonObject req;
|
||||
req.insert("accessToken", getMojangAccount()->accessToken());
|
||||
req.insert("accessToken", m_account->m_accessToken);
|
||||
return req;
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ class ValidateTask : public YggdrasilTask
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ValidateTask(MojangAccountPtr account, QObject *parent = 0);
|
||||
ValidateTask(MojangAccount *account, QObject *parent = 0);
|
||||
|
||||
protected:
|
||||
virtual QJsonObject getRequestContent() const;
|
||||
|
@ -83,10 +83,7 @@ void MojangAccountList::removeAccount(QModelIndex index)
|
||||
|
||||
MojangAccountPtr MojangAccountList::activeAccount() const
|
||||
{
|
||||
if (m_activeAccount.isEmpty())
|
||||
return nullptr;
|
||||
else
|
||||
return findAccount(m_activeAccount);
|
||||
return m_activeAccount;
|
||||
}
|
||||
|
||||
void MojangAccountList::setActiveAccount(const QString &username)
|
||||
@ -94,14 +91,14 @@ void MojangAccountList::setActiveAccount(const QString &username)
|
||||
beginResetModel();
|
||||
if (username.isEmpty())
|
||||
{
|
||||
m_activeAccount = "";
|
||||
m_activeAccount = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (MojangAccountPtr account : m_accounts)
|
||||
{
|
||||
if (account->username() == username)
|
||||
m_activeAccount = username;
|
||||
m_activeAccount = account;
|
||||
}
|
||||
}
|
||||
endResetModel();
|
||||
@ -152,7 +149,7 @@ QVariant MojangAccountList::data(const QModelIndex &index, int role) const
|
||||
switch (index.column())
|
||||
{
|
||||
case ActiveColumn:
|
||||
return account->username() == m_activeAccount;
|
||||
return account == m_activeAccount;
|
||||
|
||||
case NameColumn:
|
||||
return account->username();
|
||||
@ -297,11 +294,9 @@ bool MojangAccountList::loadList(const QString &filePath)
|
||||
QLOG_WARN() << "Failed to load an account.";
|
||||
}
|
||||
}
|
||||
endResetModel();
|
||||
|
||||
// Load the active account.
|
||||
m_activeAccount = root.value("activeAccount").toString("");
|
||||
|
||||
m_activeAccount = findAccount(root.value("activeAccount").toString(""));
|
||||
endResetModel();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -336,8 +331,11 @@ bool MojangAccountList::saveList(const QString &filePath)
|
||||
// Insert the account list into the root object.
|
||||
root.insert("accounts", accounts);
|
||||
|
||||
if(m_activeAccount)
|
||||
{
|
||||
// Save the active account.
|
||||
root.insert("activeAccount", m_activeAccount);
|
||||
root.insert("activeAccount", m_activeAccount->username());
|
||||
}
|
||||
|
||||
// Create a JSON document object to convert our JSON to bytes.
|
||||
QJsonDocument doc(root);
|
||||
|
@ -161,10 +161,9 @@ protected:
|
||||
QList<MojangAccountPtr> m_accounts;
|
||||
|
||||
/*!
|
||||
* Username of the account that is currently active.
|
||||
* Empty string if no account is active.
|
||||
* Account that is currently active.
|
||||
*/
|
||||
QString m_activeAccount;
|
||||
MojangAccountPtr m_activeAccount;
|
||||
|
||||
//! Path to the account list file. Empty string if there isn't one.
|
||||
QString m_listFilePath;
|
||||
|
Loading…
Reference in New Issue
Block a user