feature: Add support for authlibinjector

Signed-off-by: Lenny McLennington <lenny@sneed.church>
This commit is contained in:
Lenny McLennington 2023-08-30 02:29:32 -04:00
parent b995074440
commit 290ccebc29
27 changed files with 441 additions and 21 deletions

View File

@ -818,6 +818,7 @@ Application::Application(int &argc, char **argv) : QApplication(argc, argv)
m_metacache->addBase("translations", QDir("translations").absolutePath());
m_metacache->addBase("icons", QDir("cache/icons").absolutePath());
m_metacache->addBase("meta", QDir("meta").absolutePath());
m_metacache->addBase("authlibinjector", QDir("cache/authlibinjector").absolutePath());
m_metacache->Load();
qDebug() << "<> Cache initialized.";
}

View File

@ -205,11 +205,15 @@ set(MINECRAFT_SOURCES
minecraft/auth/flows/AuthFlow.h
minecraft/auth/flows/Mojang.cpp
minecraft/auth/flows/Mojang.h
minecraft/auth/flows/AuthlibInjector.cpp
minecraft/auth/flows/AuthlibInjector.h
minecraft/auth/flows/MSA.cpp
minecraft/auth/flows/MSA.h
minecraft/auth/flows/Offline.cpp
minecraft/auth/flows/Offline.h
minecraft/auth/steps/AuthlibInjectorStep.cpp
minecraft/auth/steps/AuthlibInjectorStep.h
minecraft/auth/steps/OfflineStep.cpp
minecraft/auth/steps/OfflineStep.h
minecraft/auth/steps/EntitlementsStep.cpp
@ -249,6 +253,8 @@ set(MINECRAFT_SOURCES
minecraft/launch/ClaimAccount.cpp
minecraft/launch/ClaimAccount.h
minecraft/launch/ConfigureAuthlibInjector.cpp
minecraft/launch/ConfigureAuthlibInjector.h
minecraft/launch/CreateGameFolders.cpp
minecraft/launch/CreateGameFolders.h
minecraft/launch/ModMinecraftJar.cpp

View File

@ -60,6 +60,7 @@
#include "launch/steps/QuitAfterGameStop.h"
#include "minecraft/launch/LauncherPartLaunch.h"
#include "minecraft/launch/ConfigureAuthlibInjector.h"
#include "minecraft/launch/DirectJavaLaunch.h"
#include "minecraft/launch/ModMinecraftJar.h"
#include "minecraft/launch/ClaimAccount.h"
@ -388,6 +389,11 @@ QStringList MinecraftInstance::javaArguments()
{
QStringList args;
if (!m_authlibinjector_javaagent->isNull())
{
args.append(QString("-javaagent:%1").arg(*m_authlibinjector_javaagent));
}
// custom args go first. we want to override them if we have our own here.
args.append(extraArguments());
@ -990,6 +996,12 @@ shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPt
process->appendStep(step);
}
*m_authlibinjector_javaagent = QString();
if (!session->authlib_injector_base_url.isNull())
{
process->appendStep(new ConfigureAuthlibInjector(pptr, session->authlib_injector_base_url, m_authlibinjector_javaagent));
}
// if we aren't in offline mode,.
if(session->status != AuthSession::PlayableOffline)
{

View File

@ -173,6 +173,7 @@ protected: // data
mutable std::shared_ptr<TexturePackFolderModel> m_texture_pack_list;
mutable std::shared_ptr<WorldList> m_world_list;
mutable std::shared_ptr<GameOptions> m_game_options;
mutable std::shared_ptr<QString> m_authlibinjector_javaagent = std::make_shared<QString>();
};
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;

View File

@ -350,6 +350,8 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
type = AccountType::MSA;
} else if (typeS == "Mojang") {
type = AccountType::Mojang;
} else if (typeS == "Authlib-Injector") {
type = AccountType::AuthlibInjector;
} else if (typeS == "Offline") {
type = AccountType::Offline;
} else {
@ -362,6 +364,10 @@ bool AccountData::resumeStateFromV3(QJsonObject data) {
canMigrateToMSA = data.value("canMigrateToMSA").toBool(false);
}
if(type == AccountType::AuthlibInjector) {
authlibInjectorBaseUrl = data.value("authlibInjectorUrl").toString();
}
if(type == AccountType::MSA) {
auto clientIDV = data.value("msa-client-id");
if (clientIDV.isString()) {
@ -405,8 +411,10 @@ QJsonObject AccountData::saveState() const {
tokenToJSONV3(output, userToken, "utoken");
tokenToJSONV3(output, xboxApiToken, "xrp-main");
tokenToJSONV3(output, mojangservicesToken, "xrp-mc");
}
else if (type == AccountType::Offline) {
} else if (type == AccountType::AuthlibInjector) {
output["type"] = "Authlib-Injector";
output["authlibInjectorUrl"] = authlibInjectorBaseUrl;
} else if (type == AccountType::Offline) {
output["type"] = "Offline";
}
@ -428,14 +436,14 @@ QString AccountData::accessToken() const {
}
QString AccountData::clientToken() const {
if(type != AccountType::Mojang) {
if(type != AccountType::Mojang && type != AccountType::AuthlibInjector) {
return QString();
}
return yggdrasilToken.extra["clientToken"].toString();
}
void AccountData::setClientToken(QString clientToken) {
if(type != AccountType::Mojang) {
if(type != AccountType::Mojang && type != AccountType::AuthlibInjector) {
return;
}
yggdrasilToken.extra["clientToken"] = clientToken;
@ -449,7 +457,7 @@ void AccountData::generateClientTokenIfMissing() {
}
void AccountData::invalidateClientToken() {
if(type != AccountType::Mojang) {
if(type != AccountType::Mojang && type != AccountType::AuthlibInjector) {
return;
}
yggdrasilToken.extra["clientToken"] = QUuid::createUuid().toString().remove(QRegularExpression("[{-}]"));
@ -470,6 +478,7 @@ QString AccountData::profileName() const {
QString AccountData::accountDisplayString() const {
switch(type) {
case AccountType::AuthlibInjector:
case AccountType::Mojang: {
return userName();
}

View File

@ -74,6 +74,7 @@ struct MinecraftProfile {
enum class AccountType {
MSA,
Mojang,
AuthlibInjector,
Offline
};
@ -115,6 +116,9 @@ struct AccountData {
QString lastError() const;
AccountType type = AccountType::MSA;
QString authlibInjectorBaseUrl;
QString authlibInjectorApiLocation;
bool legacy = false;
bool canMigrateToMSA = false;

View File

@ -38,8 +38,12 @@ struct AuthSession
QString player_name;
// profile ID
QString uuid;
// 'legacy' or 'mojang', depending on account type
// 'legacy' or 'mojang' or 'authlib-injector', depending on account type
QString user_type;
// If not using authlib injector, this is blank.
QString authlib_injector_base_url;
// Did the auth server reply?
bool auth_server_online = false;
// Did the user request online mode?

View File

@ -50,7 +50,9 @@
#include "flows/MSA.h"
#include "flows/Mojang.h"
#include "flows/AuthlibInjector.h"
#include "flows/Offline.h"
#include "minecraft/auth/AccountData.h"
MinecraftAccount::MinecraftAccount(QObject* parent) : QObject(parent) {
data.internalId = QUuid::createUuid().toString().remove(QRegularExpression("[{}-]"));
@ -82,6 +84,16 @@ MinecraftAccountPtr MinecraftAccount::createFromUsername(const QString &username
return account;
}
MinecraftAccountPtr MinecraftAccount::createAuthlibInjectorFromUsername(const QString &username, QString baseUrl)
{
MinecraftAccountPtr account = createFromUsername(username);
account->data.type = AccountType::AuthlibInjector;
account->data.authlibInjectorBaseUrl = baseUrl;
account->data.minecraftEntitlement.ownsMinecraft = true;
account->data.minecraftEntitlement.canPlayMinecraft = true;
return account;
}
MinecraftAccountPtr MinecraftAccount::createBlankMSA()
{
MinecraftAccountPtr account(new MinecraftAccount());
@ -132,7 +144,14 @@ QPixmap MinecraftAccount::getFace() const {
shared_qobject_ptr<AccountTask> MinecraftAccount::login(QString password) {
Q_ASSERT(m_currentTask.get() == nullptr);
m_currentTask.reset(new MojangLogin(&data, password));
if (data.type == AccountType::Mojang)
{
m_currentTask.reset(new MojangLogin(&data, password));
}
else if (data.type == AccountType::AuthlibInjector)
{
m_currentTask.reset(new AuthlibInjectorLogin(&data, password));
}
connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded()));
connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString)));
connect(m_currentTask.get(), &Task::aborted, this, [this]{ authFailed(tr("Aborted")); });
@ -173,6 +192,9 @@ shared_qobject_ptr<AccountTask> MinecraftAccount::refresh() {
else if(data.type == AccountType::Offline) {
m_currentTask.reset(new OfflineRefresh(&data));
}
else if(data.type == AccountType::AuthlibInjector) {
m_currentTask.reset(new AuthlibInjectorRefresh(&data));
}
else {
m_currentTask.reset(new MojangRefresh(&data));
}
@ -300,8 +322,9 @@ void MinecraftAccount::fillSession(AuthSessionPtr session)
session->player_name = data.profileName();
// profile ID
session->uuid = data.profileId();
// 'legacy' or 'mojang', depending on account type
// 'legacy' or 'mojang', or 'authlib-injector' depending on account type
session->user_type = typeString();
session->authlib_injector_base_url = data.authlibInjectorBaseUrl;
if (!session->access_token.isEmpty())
{
session->session = "token:" + data.accessToken() + ":" + data.profileId();

View File

@ -91,6 +91,8 @@ public: /* construction */
static MinecraftAccountPtr createFromUsername(const QString &username);
static MinecraftAccountPtr createAuthlibInjectorFromUsername(const QString &username, QString baseUrl);
static MinecraftAccountPtr createBlankMSA();
static MinecraftAccountPtr createOffline(const QString &username);
@ -177,6 +179,10 @@ public: /* queries */
return "msa";
}
break;
case AccountType::AuthlibInjector: {
return "authlib-injector";
}
break;
case AccountType::Offline: {
return "offline";
}

View File

@ -27,6 +27,25 @@
#include "Application.h"
QString Yggdrasil::getBaseUrl()
{
switch (m_data->type)
{
case AccountType::Mojang: {
return "https://authserver.mojang.com";
}
case AccountType::AuthlibInjector: {
return m_data->authlibInjectorApiLocation + "/authserver";
}
// Silence warnings about unhandled enum values for values we know shouldn't be handled.
case AccountType::MSA:
case AccountType::Offline:
break;
}
return "";
}
Yggdrasil::Yggdrasil(AccountData *data, QObject *parent)
: AccountTask(data, parent)
{
@ -84,7 +103,7 @@ void Yggdrasil::refresh() {
req.insert("requestUser", false);
QJsonDocument doc(req);
QUrl reqUrl("https://authserver.mojang.com/refresh");
QUrl reqUrl = getBaseUrl() + "/refresh";
QByteArray requestData = doc.toJson();
sendRequest(reqUrl, requestData);
@ -129,7 +148,8 @@ void Yggdrasil::login(QString password) {
QJsonDocument doc(req);
QUrl reqUrl("https://authserver.mojang.com/authenticate");
QUrl reqUrl = getBaseUrl() + "/authenticate";
qDebug() << "baseurl = " << getBaseUrl() << "requrl = " << reqUrl;
QNetworkRequest netRequest(reqUrl);
QByteArray requestData = doc.toJson();

View File

@ -90,6 +90,7 @@ public slots:
private:
void sendRequest(QUrl endpoint, QByteArray content);
QString getBaseUrl();
protected:
QNetworkReply *m_netReply = nullptr;

View File

@ -0,0 +1,29 @@
#include "AuthlibInjector.h"
#include "minecraft/auth/steps/AuthlibInjectorStep.h"
#include "minecraft/auth/steps/MinecraftProfileStepMojang.h"
#include "minecraft/auth/steps/YggdrasilStep.h"
#include "minecraft/auth/steps/MinecraftProfileStep.h"
#include "minecraft/auth/steps/MigrationEligibilityStep.h"
#include "minecraft/auth/steps/GetSkinStep.h"
AuthlibInjectorRefresh::AuthlibInjectorRefresh(
AccountData *data,
QObject *parent
) : AuthFlow(data, parent) {
m_steps.append(new AuthlibInjectorStep(m_data));
m_steps.append(new YggdrasilStep(m_data, QString()));
m_steps.append(new MinecraftProfileStepMojang(m_data));
m_steps.append(new GetSkinStep(m_data));
}
AuthlibInjectorLogin::AuthlibInjectorLogin(
AccountData *data,
QString password,
QObject *parent
): AuthFlow(data, parent), m_password(password) {
m_steps.append(new AuthlibInjectorStep(m_data));
m_steps.append(new YggdrasilStep(m_data, m_password));
m_steps.append(new MinecraftProfileStepMojang(m_data));
m_steps.append(new GetSkinStep(m_data));
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "AuthFlow.h"
class AuthlibInjectorRefresh : public AuthFlow
{
Q_OBJECT
public:
explicit AuthlibInjectorRefresh(
AccountData *data,
QObject *parent = 0
);
};
class AuthlibInjectorLogin : public AuthFlow
{
Q_OBJECT
public:
explicit AuthlibInjectorLogin(
AccountData *data,
QString password,
QObject *parent = 0
);
private:
QString m_password;
};

View File

@ -0,0 +1,58 @@
#include "AuthlibInjectorStep.h"
#include "Application.h"
#include <iostream>
#include <QNetworkRequest>
#include <QUuid>
AuthlibInjectorStep::AuthlibInjectorStep(AccountData* data) : AuthStep(data) {
}
AuthlibInjectorStep::~AuthlibInjectorStep() noexcept = default;
QString AuthlibInjectorStep::describe() {
return tr("Fetching authlib injector API URL");
}
void AuthlibInjectorStep::perform() {
// Default to the same as the base URL
QUrl url;
url.setScheme("https");
url.setAuthority(m_data->authlibInjectorBaseUrl);
qDebug() << url << url.toString() << url.isLocalFile();
m_data->authlibInjectorApiLocation = url.toString();
QNetworkRequest request = QNetworkRequest(url);
m_reply.reset( APPLICATION->network()->get(request));
connect(m_reply.get(), &QNetworkReply::finished, this, &AuthlibInjectorStep::onRequestDone);
qDebug() << "Fetching authlib injector API URL";
}
void AuthlibInjectorStep::rehydrate() {
// NOOP, for now. We only save bools and there's nothing to check.
}
void AuthlibInjectorStep::onRequestDone() {
if (m_reply->hasRawHeader("x-authlib-injector-api-location"))
{
QString authlibInjectorApiLocationHeader = m_reply->rawHeader("x-authlib-injector-api-location");
QUrl url = authlibInjectorApiLocationHeader;
if (!url.isValid())
{
qDebug() << "Invalid Authlib Injector API URL specified by server: " << authlibInjectorApiLocationHeader;
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Invalid authlib injector API URL"));
}
else
{
m_data->authlibInjectorApiLocation = authlibInjectorApiLocationHeader;
qDebug() << "Authlib injector API URL: " << m_data->authlibInjectorApiLocation;
emit finished(AccountTaskState::STATE_WORKING, tr("Fetched authlib injector API URL"));
}
}
else
{
qDebug() << "Authlib injector API URL not found";
emit finished(AccountTaskState::STATE_WORKING, tr("Authlib injector API URL not found, defaulting to the supplied base URL"));
}
}

View File

@ -0,0 +1,24 @@
#pragma once
#include <QObject>
#include "QObjectPtr.h"
#include "minecraft/auth/AuthStep.h"
class AuthlibInjectorStep : public AuthStep {
Q_OBJECT
public:
explicit AuthlibInjectorStep(AccountData *data);
virtual ~AuthlibInjectorStep() noexcept;
void perform() override;
void rehydrate() override;
QString describe() override;
private slots:
void onRequestDone();
private:
std::unique_ptr<QNetworkReply> m_reply;
};

View File

@ -7,7 +7,21 @@
#include "net/NetUtils.h"
MinecraftProfileStep::MinecraftProfileStep(AccountData* data) : AuthStep(data) {
switch (m_data->type)
{
case AccountType::Mojang: {
baseUrl = "https://api.minecraftservices.com";
break;
}
case AccountType::AuthlibInjector: {
baseUrl = m_data->authlibInjectorApiLocation + "/minecraftservices";
break;
}
// Silence warnings about unhandled enum values for values we know shouldn't be handled.
case AccountType::MSA:
case AccountType::Offline:
break;
}
}
MinecraftProfileStep::~MinecraftProfileStep() noexcept = default;
@ -18,7 +32,7 @@ QString MinecraftProfileStep::describe() {
void MinecraftProfileStep::perform() {
auto url = QUrl("https://api.minecraftservices.com/minecraft/profile");
QUrl url = baseUrl + "/minecraft/profile";
QNetworkRequest request = QNetworkRequest(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8());

View File

@ -19,4 +19,7 @@ public:
private slots:
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
private:
QString baseUrl;
};

View File

@ -6,8 +6,24 @@
#include "minecraft/auth/Parsers.h"
#include "net/NetUtils.h"
MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {
MinecraftProfileStepMojang::MinecraftProfileStepMojang(AccountData* data) : AuthStep(data) {}
QString MinecraftProfileStepMojang::getBaseUrl()
{
switch (m_data->type)
{
case AccountType::Mojang: {
return "https://sessionserver.mojang.com";
}
case AccountType::AuthlibInjector: {
return m_data->authlibInjectorApiLocation + "/sessionserver";
}
// Silence warnings about unhandled enum values for values we know shouldn't be handled.
case AccountType::MSA:
case AccountType::Offline:
break;
}
return "";
}
MinecraftProfileStepMojang::~MinecraftProfileStepMojang() noexcept = default;
@ -24,7 +40,7 @@ void MinecraftProfileStepMojang::perform() {
}
// use session server instead of profile due to profile endpoint being locked for locked Mojang accounts
QUrl url = QUrl("https://sessionserver.mojang.com/session/minecraft/profile/" + m_data->minecraftProfile.id);
QUrl url = getBaseUrl() + "/session/minecraft/profile/" + m_data->minecraftProfile.id;
QNetworkRequest req = QNetworkRequest(url);
AuthRequest *request = new AuthRequest(this);
connect(request, &AuthRequest::finished, this, &MinecraftProfileStepMojang::onRequestDone);

View File

@ -19,4 +19,7 @@ public:
private slots:
void onRequestDone(QNetworkReply::NetworkError, QByteArray, QList<QNetworkReply::RawHeaderPair>);
private:
QString getBaseUrl();
};

View File

@ -0,0 +1,80 @@
#include "ConfigureAuthlibInjector.h"
#include <launch/LaunchTask.h>
#include <QDir>
#include <QFileInfo>
#include <QJsonDocument>
#include <Qt>
#include "Application.h"
#include "minecraft/auth/AccountList.h"
#include "net/ChecksumValidator.h"
#include "net/Download.h"
#include "net/HttpMetaCache.h"
#include "net/NetAction.h"
ConfigureAuthlibInjector::ConfigureAuthlibInjector(LaunchTask* parent,
QString authlibinjector_base_url,
std::shared_ptr<QString> javaagent_arg)
: LaunchStep(parent), m_javaagent_arg{ javaagent_arg }, m_authlibinjector_base_url{ authlibinjector_base_url }
{}
void ConfigureAuthlibInjector::executeTask()
{
auto downloadFailed = [this] (QString reason) {
return emitFailed(QString("Download failed: %1").arg(reason));
};
auto entry = APPLICATION->metacache()->resolveEntry("authlibinjector", "latest.json");
entry->setStale(true);
m_job = std::make_unique<NetJob>("Download authlibinjector latest.json", APPLICATION->network());
auto latestJsonDl =
Net::Download::makeCached(QUrl("https://authlib-injector.yushi.moe/artifact/latest.json"), entry, Net::Download::Option::NoOptions);
m_job->addNetAction(latestJsonDl);
connect(m_job.get(), &NetJob::succeeded, this, [this, entry, downloadFailed] {
QFile authlibInjectorLatestJson = entry->getFullPath();
authlibInjectorLatestJson.open(QIODevice::ReadOnly);
if (!authlibInjectorLatestJson.isOpen())
return emitFailed(QString("Failed to open authlib-injector info json: %1").arg(authlibInjectorLatestJson.errorString()));
QJsonParseError json_parse_error;
QJsonDocument doc = QJsonDocument::fromJson(authlibInjectorLatestJson.readAll(), &json_parse_error);
if (json_parse_error.error != QJsonParseError::NoError)
return emitFailed(QString("Failed to parse authlib-injector info json: %1").arg(json_parse_error.errorString()));
if (!doc.isObject())
return emitFailed(QString("Failed to parse authlib-injector info json: not a json object"));
QJsonObject obj = doc.object();
QString authlibInjectorJarUrl = obj["download_url"].toString();
if (authlibInjectorJarUrl.isNull())
return emitFailed(QString("Failed to parse authlib-injector info json: download url missing"));
QString sha256Sum = obj["checksums"].toObject()["sha256"].toString();
if (sha256Sum.isNull())
return emitFailed("Failed to parse authlib-injector info json: sha256 checksum missing");
auto sha256SumRaw = QByteArray::fromHex(sha256Sum.toLatin1());
QString filename = QFileInfo(authlibInjectorJarUrl).fileName();
auto javaAgentEntry = APPLICATION->metacache()->resolveEntry("authlibinjector", filename);
m_job = std::make_unique<NetJob>("Download authlibinjector java agent", APPLICATION->network());
auto javaAgentDl = Net::Download::makeCached(QUrl(authlibInjectorJarUrl), javaAgentEntry, Net::Download::Option::MakeEternal);
javaAgentDl->addValidator(new Net::ChecksumValidator(QCryptographicHash::Sha256, sha256SumRaw));
m_job->addNetAction(javaAgentDl);
connect(m_job.get(), &NetJob::succeeded, this, [this, javaAgentEntry] {
auto path = javaAgentEntry->getFullPath();
qDebug() << path;
*m_javaagent_arg = QString("%1=%2").arg(path).arg(m_authlibinjector_base_url);
emitSucceeded();
});
connect(m_job.get(), &NetJob::failed, this, downloadFailed);
m_job->start();
},
// This slot can't run instantly because it needs to wait for the netjob's code to stop running
// Since it will destroy the old netjob by reassigning the unique_ptr
Qt::QueuedConnection);
connect(m_job.get(), &NetJob::failed, this, downloadFailed);
m_job->start();
}
void ConfigureAuthlibInjector::finalize() {}

View File

@ -0,0 +1,24 @@
#pragma once
#include <launch/LaunchStep.h>
#include <minecraft/auth/MinecraftAccount.h>
#include "net/NetJob.h"
class ConfigureAuthlibInjector: public LaunchStep
{
Q_OBJECT
public:
explicit ConfigureAuthlibInjector(LaunchTask *parent, QString authlibinjector_base_url, std::shared_ptr<QString> javaagent_arg);
virtual ~ConfigureAuthlibInjector() {};
void executeTask() override;
void finalize() override;
bool canAbort() const override
{
return false;
}
private:
std::unique_ptr<NetJob> m_job;
std::shared_ptr<QString> m_javaagent_arg;
QString m_authlibinjector_base_url;
};

View File

@ -20,9 +20,10 @@
#include <QtWidgets/QPushButton>
LoginDialog::LoginDialog(QWidget *parent) : QDialog(parent), ui(new Ui::LoginDialog)
LoginDialog::LoginDialog(QWidget *parent, AccountType type) : QDialog(parent), ui(new Ui::LoginDialog), m_accountType{type}
{
ui->setupUi(this);
ui->authlibInjectorBaseTextBox->setVisible(m_accountType == AccountType::AuthlibInjector);
ui->progressBar->setVisible(false);
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
@ -42,7 +43,14 @@ void LoginDialog::accept()
ui->progressBar->setVisible(true);
// Setup the login task and start it
m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
if (m_accountType == AccountType::Mojang)
{
m_account = MinecraftAccount::createFromUsername(ui->userTextBox->text());
}
else if (m_accountType == AccountType::AuthlibInjector)
{
m_account = MinecraftAccount::createAuthlibInjectorFromUsername(ui->userTextBox->text(), ui->authlibInjectorBaseTextBox->text());
}
m_loginTask = m_account->login(ui->passTextBox->text());
connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed);
connect(m_loginTask.get(), &Task::succeeded, this, &LoginDialog::onTaskSucceeded);
@ -106,10 +114,9 @@ void LoginDialog::onTaskProgress(qint64 current, qint64 total)
ui->progressBar->setValue(current);
}
// Public interface
MinecraftAccountPtr LoginDialog::newAccount(QWidget *parent, QString msg)
MinecraftAccountPtr LoginDialog::newAccount(QWidget *parent, QString msg, AccountType type)
{
LoginDialog dlg(parent);
LoginDialog dlg(parent, type);
dlg.ui->label->setText(msg);
if (dlg.exec() == QDialog::Accepted)
{

View File

@ -18,6 +18,7 @@
#include <QtWidgets/QDialog>
#include <QtCore/QEventLoop>
#include "minecraft/auth/AccountData.h"
#include "minecraft/auth/MinecraftAccount.h"
#include "tasks/Task.h"
@ -33,10 +34,13 @@ class LoginDialog : public QDialog
public:
~LoginDialog();
static MinecraftAccountPtr newAccount(QWidget *parent, QString message);
/*
* @param type: Mojang or Authlib
*/
static MinecraftAccountPtr newAccount(QWidget *parent, QString message, AccountType type = AccountType::Mojang);
private:
explicit LoginDialog(QWidget *parent = 0);
explicit LoginDialog(QWidget *parent = 0, AccountType type = AccountType::Mojang);
void setUserInputsEnabled(bool enable);
@ -56,4 +60,5 @@ private:
Ui::LoginDialog *ui;
MinecraftAccountPtr m_account;
Task::Ptr m_loginTask;
AccountType m_accountType;
};

View File

@ -33,6 +33,13 @@
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="authlibInjectorBaseTextBox">
<property name="placeholderText">
<string>AuthlibInjector base URL (e.g. ely.by)</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="userTextBox">
<property name="placeholderText">

View File

@ -157,6 +157,36 @@ void AccountListPage::on_actionAddMojang_triggered()
}
}
void AccountListPage::on_actionAddAuthlibInjector_triggered()
{
if (!m_accounts->drmCheck()) {
QMessageBox::warning(
this,
tr("Error"),
tr(
"You must add a Microsoft or Mojang account that owns Minecraft before you can add an Authlib Injector account."
"<br><br>"
"If you have lost your account you can contact Microsoft for support."
)
);
return;
}
MinecraftAccountPtr account = LoginDialog::newAccount(
this,
tr("Please enter the AuthlibInjector base URL, and enter your account email and password to add your account."),
AccountType::AuthlibInjector
);
if (account)
{
m_accounts->addAccount(account);
if (m_accounts->count() == 1) {
m_accounts->setDefaultAccount(account);
}
}
}
void AccountListPage::on_actionAddMicrosoft_triggered()
{
if(BuildConfig.BUILD_PLATFORM == "osx64") {

View File

@ -83,6 +83,7 @@ public:
public slots:
void on_actionAddMojang_triggered();
void on_actionAddAuthlibInjector_triggered();
void on_actionAddMicrosoft_triggered();
void on_actionAddOffline_triggered();
void on_actionRemove_triggered();

View File

@ -54,6 +54,7 @@
</attribute>
<addaction name="actionAddMicrosoft"/>
<addaction name="actionAddMojang"/>
<addaction name="actionAddAuthlibInjector"/>
<addaction name="actionAddOffline"/>
<addaction name="actionRefresh"/>
<addaction name="actionRemove"/>
@ -68,6 +69,11 @@
<string>Add &amp;Mojang</string>
</property>
</action>
<action name="actionAddAuthlibInjector">
<property name="text">
<string>Add Authlib-&amp;Injector</string>
</property>
</action>
<action name="actionRemove">
<property name="text">
<string>Remo&amp;ve</string>