Add offline mode support

This commit is contained in:
bexnoss 2022-01-12 14:26:02 +01:00
parent d4b522b6cb
commit a1ff3b1ee3
12 changed files with 144 additions and 5 deletions

View File

@ -221,7 +221,11 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/Mojang.h
minecraft/auth/flows/MSA.cpp
minecraft/auth/flows/MSA.h
minecraft/auth/flows/Offline.cpp
minecraft/auth/flows/Offline.h
minecraft/auth/steps/OfflineStep.cpp
minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/EntitlementsStep.cpp
minecraft/auth/steps/EntitlementsStep.h
minecraft/auth/steps/GetSkinStep.cpp

View File

@ -116,6 +116,12 @@ void LaunchController::login() {
m_session->wants_online = m_online;
m_accountToUse->fillSession(m_session);
// Launch immediately in true offline mode
if(m_accountToUse->isOffline()) {
launchInstance();
return;
}
switch(m_accountToUse->accountState()) {
case AccountState::Offline: {
m_session->wants_online = false;

View File

@ -314,6 +314,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
type = AccountType::MSA;
} else if (typeS == "Mojang") {
type = AccountType::Mojang;
} else if (typeS == "Offline") {
type = AccountType::Offline;
} else {
qWarning() << "Failed to parse account data: type is not recognized.";
return false;
@ -363,6 +365,9 @@ QJsonObject AccountData::saveState() const {
tokenToJSONV3(output, xboxApiToken, "xrp-main");
tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
}
else if (type == AccountType::Offline) {
output["type"] = "Offline";
}
tokenToJSONV3(output, yggdrasilToken, "ygg");
profileToJSONV3(output, minecraftProfile, "profile");
@ -371,7 +376,7 @@ QJsonObject AccountData::saveState() const {
}
QString AccountData::userName() const {
if(type != AccountType::Mojang) {
if(type == AccountType::MSA) {
return QString();
}
return yggdrasilToken.extra["userName"].toString();
@ -427,6 +432,9 @@ QString AccountData::accountDisplayString() const {
case AccountType::Mojang: {
return userName();
}
case AccountType::Offline: {
return userName();
}
case AccountType::MSA: {
if(xboxApiToken.extra.contains("gtg")) {
return xboxApiToken.extra["gtg"].toString();

View File

@ -38,7 +38,8 @@ struct MinecraftProfile {
enum class AccountType {
MSA,
Mojang
Mojang,
Offline
};
enum class AccountState {

View File

@ -302,7 +302,7 @@ QVariant AccountList::data(const QModelIndex &index, int role) const
}
case MigrationColumn: {
if(account->isMSA()) {
if(account->isMSA() || account->isOffline()) {
return tr("N/A", "Can Migrate?");
}
if (account->canMigrate()) {

View File

@ -30,6 +30,7 @@
#include "flows/MSA.h"
#include "flows/Mojang.h"
#include "flows/Offline.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
data.internalId = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
@ -68,6 +69,23 @@ MinecraftAccountPtr MinecraftAccount::createBlankMSA()
return account;
}
MinecraftAccountPtr MinecraftAccount::createOffline(const QString &username)
{
MinecraftAccountPtr account = new MinecraftAccount();
account->data.type = AccountType::Offline;
account->data.yggdrasilToken.token = "offline";
account->data.yggdrasilToken.validity = Katabasis::Validity::Certain;
account->data.yggdrasilToken.issueInstant = QDateTime::currentDateTimeUtc();
account->data.yggdrasilToken.extra["userName"] = username;
account->data.yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
account->data.minecraftEntitlement.ownsMinecraft = true;
account->data.minecraftEntitlement.canPlayMinecraft = true;
account->data.minecraftProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}-]"));
account->data.minecraftProfile.name = username;
account->data.minecraftProfile.validity = Katabasis::Validity::Certain;
return account;
}
QJsonObject MinecraftAccount::saveToJson() const
{
@ -111,6 +129,16 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::loginMSA() {
return m_currentTask;
}
shared_qobject_ptr<AccountTask> MinecraftAccount::loginOffline() {
Q_ASSERT(m_currentTask.get() == nullptr);
m_currentTask.reset(new OfflineLogin(&data));
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
emit activityChanged(true);
return m_currentTask;
}
shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
if(m_currentTask) {
return m_currentTask;
@ -119,6 +147,9 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
if(data.type == AccountType::MSA) {
m_currentTask.reset(new MSASilent(&data));
}
if(data.type == AccountType::Offline) {
m_currentTask.reset(new OfflineRefresh(&data));
}
else {
m_currentTask.reset(new MojangRefresh(&data));
}

View File

@ -73,6 +73,8 @@ public: /* construction */
static MinecraftAccountPtr createBlankMSA();
static MinecraftAccountPtr createOffline(const QString &username);
static MinecraftAccountPtr loadFromJsonV2(const QJsonObject &json);
static MinecraftAccountPtr loadFromJsonV3(const QJsonObject &json);
@ -89,6 +91,8 @@ public: /* manipulation */
shared_qobject_ptr<AccountTask> loginMSA();
shared_qobject_ptr<AccountTask> loginOffline();
shared_qobject_ptr<AccountTask> refresh();
shared_qobject_ptr<AccountTask> currentTask();
@ -128,6 +132,10 @@ public: /* queries */
return data.type == AccountType::MSA;
}
bool isOffline() const {
return data.type == AccountType::Offline;
}
bool ownsMinecraft() const {
return data.minecraftEntitlement.ownsMinecraft;
}
@ -149,6 +157,10 @@ public: /* queries */
return "msa";
}
break;
case AccountType::Offline: {
return "offline";
}
break;
default: {
return "unknown";
}

View File

@ -0,0 +1,17 @@
#include "Offline.h"
#include "minecraft/auth/steps/OfflineStep.h"
OfflineRefresh::OfflineRefresh(
AccountData *data,
QObject *parent
) : AuthFlow(data, parent) {
m_steps.append(new OfflineStep(m_data));
}
OfflineLogin::OfflineLogin(
AccountData *data,
QObject *parent
) : AuthFlow(data, parent) {
m_steps.append(new OfflineStep(m_data));
}

View File

@ -0,0 +1,22 @@
#pragma once
#include "AuthFlow.h"
class OfflineRefresh : public AuthFlow
{
Q_OBJECT
public:
explicit OfflineRefresh(
AccountData *data,
QObject *parent = 0
);
};
class OfflineLogin : public AuthFlow
{
Q_OBJECT
public:
explicit OfflineLogin(
AccountData *data,
QObject *parent = 0
);
};

View File

@ -0,0 +1,18 @@
#include "OfflineStep.h"
#include "Application.h"
OfflineStep::OfflineStep(AccountData* data) : AuthStep(data) {};
OfflineStep::~OfflineStep() noexcept = default;
QString OfflineStep::describe() {
return tr("Creating offline account.");
}
void OfflineStep::rehydrate() {
// NOOP
}
void OfflineStep::perform() {
emit finished(AccountTaskState::STATE_WORKING, tr("Created offline account."));
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <QObject>
#include "QObjectPtr.h"
#include "minecraft/auth/AuthStep.h"
#include <katabasis/DeviceFlow.h>
class OfflineStep : public AuthStep {
Q_OBJECT
public:
explicit OfflineStep(AccountData *data);
virtual ~OfflineStep() noexcept;
void perform() override;
void rehydrate() override;
QString describe() override;
};

View File

@ -42,8 +42,8 @@ void OfflineLoginDialog::accept()
ui->progressBar->setVisible(true);
// Setup the login task and start it
m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
m_loginTask = m_account->login("TODO: create offline mode account flow");
m_account = MinecraftAccount::createOffline(ui->userTextBox->text());
m_loginTask = m_account->loginOffline();
connect(m_loginTask.get(), &Task::failed, this, &OfflineLoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this, &OfflineLoginDialog::onTaskSucceeded);
connect(m_loginTask.get(), &Task::status, this, &OfflineLoginDialog::onTaskStatus);