From 6df1be94dd3f092fb6d8fae23cf8847178dcf813 Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 10 Jun 2021 17:00:54 +0300 Subject: [PATCH] Authlib injector, local auth server and ely by accounts support (#31) * Add injector * Add uuid generation for profile * Add auth server emulator * Start auth server on random port and bypass it to injector * Run injector only when account type is dummy * Clean authlib injector * Add ely by authentication * Remove old comments * Add response status text to auth server * Fix json value access as done by @maximmasterr --- api/logic/AuthServer.cpp | 79 +++++++++ api/logic/AuthServer.h | 20 +++ api/logic/BaseInstance.h | 2 +- api/logic/CMakeLists.txt | 4 + api/logic/Env.cpp | 1 + api/logic/NullInstance.h | 2 +- api/logic/minecraft/MinecraftInstance.cpp | 54 ++++--- api/logic/minecraft/MinecraftInstance.h | 4 +- api/logic/minecraft/auth/MojangAccount.cpp | 14 +- api/logic/minecraft/auth/MojangAccount.h | 4 +- api/logic/minecraft/auth/YggdrasilTask.cpp | 7 +- api/logic/minecraft/launch/InjectAuthlib.cpp | 161 +++++++++++++++++++ api/logic/minecraft/launch/InjectAuthlib.h | 70 ++++++++ api/logic/minecraft/legacy/LegacyInstance.h | 2 +- application/LaunchController.cpp | 2 +- application/LaunchController.h | 6 + application/MultiMC.cpp | 7 + application/MultiMC.h | 2 + application/dialogs/LoginDialog.cpp | 2 + application/dialogs/LoginDialog.ui | 9 ++ buildconfig/BuildConfig.h | 5 +- 21 files changed, 424 insertions(+), 33 deletions(-) create mode 100644 api/logic/AuthServer.cpp create mode 100644 api/logic/AuthServer.h create mode 100644 api/logic/minecraft/launch/InjectAuthlib.cpp create mode 100644 api/logic/minecraft/launch/InjectAuthlib.h diff --git a/api/logic/AuthServer.cpp b/api/logic/AuthServer.cpp new file mode 100644 index 00000000..7a804b40 --- /dev/null +++ b/api/logic/AuthServer.cpp @@ -0,0 +1,79 @@ +#include "AuthServer.h" + +#include +#include + +AuthServer::AuthServer(QObject *parent) : QObject(parent) +{ + m_tcpServer.reset(new QTcpServer(this)); + + connect(m_tcpServer.get(), &QTcpServer::newConnection, this, &AuthServer::newConnection); + + if (!m_tcpServer->listen(QHostAddress::LocalHost)) + { + // TODO: think about stop launching when server start fails + qCritical() << "Auth server start failed"; + } +} + +quint16 AuthServer::port() +{ + return m_tcpServer->serverPort(); +} + +void AuthServer::newConnection() +{ + QTcpSocket *tcpSocket = m_tcpServer->nextPendingConnection(); + + connect(tcpSocket, &QTcpSocket::readyRead, this, [tcpSocket]() + { + // Not the best way to process queries, but it just works + QString rawRequest = tcpSocket->readAll().data(); + QStringList requestLines = rawRequest.split("\r\n"); + QString requestPath = requestLines[0].split(" ")[1]; + + int responseStatusCode = 500; + QString responseBody = ""; + QStringList responseHeaders; + + responseHeaders << "Connection: keep-alive"; + + if (requestPath == "/") + { + responseBody = "{\"Status\":\"OK\",\"Runtime-Mode\":\"productionMode\",\"Application-Author\":\"Mojang Web Force\",\"Application-Description\":\"Mojang Public API.\",\"Specification-Version\":\"3.58.0\",\"Application-Name\":\"yggdrasil.accounts.restlet.server.public\",\"Implementation-Version\":\"3.58.0_build194\",\"Application-Owner\":\"Mojang\"}"; + responseStatusCode = 200; + responseHeaders << "Content-Type: application/json; charset=utf-8"; + } + else if (requestPath == "/sessionserver/session/minecraft/join" || requestPath == "/sessionserver/session/minecraft/hasJoined") + { + responseStatusCode = 204; + } + else + { + responseBody = "Not found"; + responseStatusCode = 404; + } + + QString responseStatusText = "Internal Server Error"; + if (responseStatusCode == 200) + responseStatusText = "OK"; + else if (responseStatusCode == 204) + responseStatusText = "No Content"; + else if (responseStatusCode == 404) + responseStatusText = "Not Found"; + + if (responseBody.length() != 0) + { + responseHeaders << ((QString) "Content-Length: %1").arg(responseBody.length()); + } + + tcpSocket->write(((QString) "HTTP/1.1 %1 %2\r\nConnection: keep-alive\r\n").arg(responseStatusCode).arg(responseStatusText).toUtf8()); + tcpSocket->write(responseHeaders.join("\r\n").toUtf8()); + tcpSocket->write("\r\n\r\n"); + tcpSocket->write(responseBody.toUtf8()); + }); + connect(tcpSocket, &QTcpSocket::disconnected, this, [tcpSocket]() + { + tcpSocket->close(); + }); +} \ No newline at end of file diff --git a/api/logic/AuthServer.h b/api/logic/AuthServer.h new file mode 100644 index 00000000..7d96f7b8 --- /dev/null +++ b/api/logic/AuthServer.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include "settings/SettingsObject.h" +#include "multimc_logic_export.h" + +class MULTIMC_LOGIC_EXPORT AuthServer: public QObject +{ +public: + explicit AuthServer(QObject *parent = 0); + + quint16 port(); + +private: + void newConnection(); + +private: + std::shared_ptr m_tcpServer; +}; diff --git a/api/logic/BaseInstance.h b/api/logic/BaseInstance.h index 64de4bb3..7f54e3db 100644 --- a/api/logic/BaseInstance.h +++ b/api/logic/BaseInstance.h @@ -148,7 +148,7 @@ public: /// returns a valid launcher (task container) virtual shared_qobject_ptr createLaunchTask( - AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0; + AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) = 0; /// returns the current launch task (if any) shared_qobject_ptr getLaunchTask(); diff --git a/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index c3322955..b682a6a8 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -4,6 +4,8 @@ include (UnitTest) set(CORE_SOURCES # LOGIC - Base classes and infrastructure + AuthServer.h + AuthServer.cpp BaseInstaller.h BaseInstaller.cpp BaseVersionList.h @@ -246,6 +248,8 @@ set(MINECRAFT_SOURCES minecraft/launch/ReconstructAssets.h minecraft/launch/ScanModFolders.cpp minecraft/launch/ScanModFolders.h + minecraft/launch/InjectAuthlib.cpp + minecraft/launch/InjectAuthlib.h minecraft/legacy/LegacyModList.h minecraft/legacy/LegacyModList.cpp diff --git a/api/logic/Env.cpp b/api/logic/Env.cpp index 71b49d95..19288a5e 100644 --- a/api/logic/Env.cpp +++ b/api/logic/Env.cpp @@ -92,6 +92,7 @@ void Env::initHttpMetaCache() m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath()); m_metacache->addBase("versions", QDir("versions").absolutePath()); m_metacache->addBase("libraries", QDir("libraries").absolutePath()); + m_metacache->addBase("injectors", QDir("injectors").absolutePath()); m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath()); m_metacache->addBase("fmllibs", QDir("mods/minecraftforge/libs").absolutePath()); m_metacache->addBase("liteloader", QDir("mods/liteloader").absolutePath()); diff --git a/api/logic/NullInstance.h b/api/logic/NullInstance.h index 94ed6c3a..f2dff37c 100644 --- a/api/logic/NullInstance.h +++ b/api/logic/NullInstance.h @@ -27,7 +27,7 @@ public: { return instanceRoot(); }; - shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override + shared_qobject_ptr createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr, quint16) override { return nullptr; } diff --git a/api/logic/minecraft/MinecraftInstance.cpp b/api/logic/minecraft/MinecraftInstance.cpp index 485e8505..80ea0cad 100644 --- a/api/logic/minecraft/MinecraftInstance.cpp +++ b/api/logic/minecraft/MinecraftInstance.cpp @@ -23,6 +23,7 @@ #include "minecraft/launch/ClaimAccount.h" #include "minecraft/launch/ReconstructAssets.h" #include "minecraft/launch/ScanModFolders.h" +#include "minecraft/launch/InjectAuthlib.h" #include "java/launch/CheckJava.h" #include "java/JavaUtils.h" #include "meta/Index.h" @@ -50,7 +51,7 @@ class OrSetting : public Setting Q_OBJECT public: OrSetting(QString id, std::shared_ptr a, std::shared_ptr b) - :Setting({id}, false), m_a(a), m_b(b) + :Setting({id}, false), m_a(a), m_b(b) { } virtual QVariant get() const @@ -297,6 +298,8 @@ QStringList MinecraftInstance::javaArguments() const { QStringList args; + args.append(m_injector->javaArg); + // custom args go first. we want to override them if we have our own here. args.append(extraArguments()); @@ -401,7 +404,7 @@ static QString replaceTokensIn(QString text, QMap with) } QStringList MinecraftInstance::processMinecraftArgs( - AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const + AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) const { auto profile = m_components->getProfile(); QString args_pattern = profile->getMinecraftArguments(); @@ -481,9 +484,9 @@ QString MinecraftInstance::createLaunchScript(AuthSessionPtr session, MinecraftS // generic minecraft params for (auto param : processMinecraftArgs( - session, - nullptr /* When using a launch script, the server parameters are handled by it*/ - )) + session, + nullptr /* When using a launch script, the server parameters are handled by it*/ + )) { launchScript += "param " + param + "\n"; } @@ -601,10 +604,10 @@ QStringList MinecraftInstance::verboseDescription(AuthSessionPtr session, Minecr out << QString("%1:").arg(label); auto modList = model.allMods(); std::sort(modList.begin(), modList.end(), [](Mod &a, Mod &b) { - auto aName = a.filename().completeBaseName(); - auto bName = b.filename().completeBaseName(); - return aName.localeAwareCompare(bName) < 0; - }); + auto aName = a.filename().completeBaseName(); + auto bName = b.filename().completeBaseName(); + return aName.localeAwareCompare(bName) < 0; + }); for(auto & mod: modList) { if(mod.type() == Mod::MOD_FOLDER) @@ -741,12 +744,12 @@ MessageLevel::Enum MinecraftInstance::guessLevel(const QString &line, MessageLev return MessageLevel::Fatal; //NOTE: this diverges from the real regexp. no unicode, the first section is + instead of * static const QString javaSymbol = "([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$][a-zA-Z\\d_$]*"; - if (line.contains("Exception in thread") + if (line.contains("Exception in thread") || line.contains(QRegularExpression("\\s+at " + javaSymbol)) || line.contains(QRegularExpression("Caused by: " + javaSymbol)) || line.contains(QRegularExpression("([a-zA-Z_$][a-zA-Z\\d_$]*\\.)+[a-zA-Z_$]?[a-zA-Z\\d_$]*(Exception|Error|Throwable)")) || line.contains(QRegularExpression("... \\d+ more$")) - ) + ) return MessageLevel::Error; return level; } @@ -810,19 +813,19 @@ shared_qobject_ptr MinecraftInstance::createUpdateTask(Net::Mode mode) { switch (mode) { - case Net::Mode::Offline: - { - return shared_qobject_ptr(new MinecraftLoadAndCheck(this)); - } - case Net::Mode::Online: - { - return shared_qobject_ptr(new MinecraftUpdate(this)); - } + case Net::Mode::Offline: + { + return shared_qobject_ptr(new MinecraftLoadAndCheck(this)); + } + case Net::Mode::Online: + { + return shared_qobject_ptr(new MinecraftUpdate(this)); + } } return nullptr; } -shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) +shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) { // FIXME: get rid of shared_from_this ... auto process = LaunchTask::create(std::dynamic_pointer_cast(shared_from_this())); @@ -914,6 +917,17 @@ shared_qobject_ptr MinecraftInstance::createLaunchTask(AuthSessionPt process->appendStep(new ReconstructAssets(pptr)); } + // authlib patch + if (session->m_accountPtr->loginType() != "mojang") + { + auto step = new InjectAuthlib(pptr, &m_injector); + if(session->m_accountPtr->loginType() == "dummy") + step->setAuthServer(((QString)"http://localhost:%1").arg(localAuthServerPort)); + else if(session->m_accountPtr->loginType() == "elyby") + step->setAuthServer(((QString) "ely.by").arg(localAuthServerPort)); + process->appendStep(step); + } + { // actually launch the game auto method = launchMethod(); diff --git a/api/logic/minecraft/MinecraftInstance.h b/api/logic/minecraft/MinecraftInstance.h index 05600797..d9d285fc 100644 --- a/api/logic/minecraft/MinecraftInstance.h +++ b/api/logic/minecraft/MinecraftInstance.h @@ -6,6 +6,7 @@ #include #include "multimc_logic_export.h" #include "minecraft/launch/MinecraftServerTarget.h" +#include "minecraft/launch/InjectAuthlib.h" class ModFolderModel; class WorldList; @@ -77,7 +78,7 @@ public: ////// Launch stuff ////// shared_qobject_ptr createUpdateTask(Net::Mode mode) override; - shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override; + shared_qobject_ptr createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) override; QStringList extraArguments() const override; QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override; QList getJarMods() const; @@ -128,6 +129,7 @@ protected: // data mutable std::shared_ptr m_texture_pack_list; mutable std::shared_ptr m_world_list; mutable std::shared_ptr m_game_options; + mutable std::shared_ptr m_injector; }; typedef std::shared_ptr MinecraftInstancePtr; diff --git a/api/logic/minecraft/auth/MojangAccount.cpp b/api/logic/minecraft/auth/MojangAccount.cpp index 0774b9a2..894cfd4a 100644 --- a/api/logic/minecraft/auth/MojangAccount.cpp +++ b/api/logic/minecraft/auth/MojangAccount.cpp @@ -28,6 +28,8 @@ #include +#include + MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) { // The JSON object must at least have a username for it to be valid. @@ -148,7 +150,7 @@ QJsonObject MojangAccount::saveToJson() const bool MojangAccount::setLoginType(const QString &loginType) { // TODO: Implement a cleaner validity check - if (loginType == "mojang" or loginType == "dummy") + if (loginType == "mojang" || loginType == "dummy" || loginType == "elyby") { m_loginType = loginType; return true; @@ -184,6 +186,14 @@ AccountStatus MojangAccount::accountStatus() const return Verified; } +QString MojangAccount::authEndpoint() const +{ + if(m_loginType == "elyby") + return BuildConfig.AUTH_BASE_ELYBY; + + return BuildConfig.AUTH_BASE_MOJANG; +} + std::shared_ptr MojangAccount::login(AuthSessionPtr session, QString password) { Q_ASSERT(m_currentTask.get() == nullptr); @@ -202,7 +212,7 @@ std::shared_ptr MojangAccount::login(AuthSessionPtr session, QStr // TODO: Proper profile support (idk how) auto dummyProfile = AccountProfile(); dummyProfile.name = m_username; - dummyProfile.id = "-"; + dummyProfile.id = QUuid::createUuid().toString().remove(QRegExp("[{}]")); m_profiles.append(dummyProfile); m_currentProfile = 0; } diff --git a/api/logic/minecraft/auth/MojangAccount.h b/api/logic/minecraft/auth/MojangAccount.h index 96a9f46b..b8e8a293 100644 --- a/api/logic/minecraft/auth/MojangAccount.h +++ b/api/logic/minecraft/auth/MojangAccount.h @@ -141,6 +141,8 @@ public: /* queries */ //! Returns whether the account is NotVerified, Verified or Online AccountStatus accountStatus() const; + QString authEndpoint() const; + signals: /** * This signal is emitted when the account changes @@ -151,7 +153,7 @@ signals: protected: /* variables */ // Authentication system used. - // Usable values: "mojang", "dummy" + // Usable values: "mojang", "dummy", "elyby" QString m_loginType; // Username taken by account. diff --git a/api/logic/minecraft/auth/YggdrasilTask.cpp b/api/logic/minecraft/auth/YggdrasilTask.cpp index 0857b46b..5b6f4919 100644 --- a/api/logic/minecraft/auth/YggdrasilTask.cpp +++ b/api/logic/minecraft/auth/YggdrasilTask.cpp @@ -25,8 +25,6 @@ #include -#include - #include YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent) @@ -42,8 +40,9 @@ void YggdrasilTask::executeTask() // Get the content of the request we're going to send to the server. QJsonDocument doc(getRequestContent()); - QUrl reqUrl(BuildConfig.AUTH_BASE + getEndpoint()); - QNetworkRequest netRequest(reqUrl); + QUrl reqUrl(m_account->authEndpoint() + getEndpoint()); + qDebug() << m_account->authEndpoint() + getEndpoint(); + QNetworkRequest netRequest(reqUrl); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QByteArray requestData = doc.toJson(); diff --git a/api/logic/minecraft/launch/InjectAuthlib.cpp b/api/logic/minecraft/launch/InjectAuthlib.cpp new file mode 100644 index 00000000..c37ed4e5 --- /dev/null +++ b/api/logic/minecraft/launch/InjectAuthlib.cpp @@ -0,0 +1,161 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "InjectAuthlib.h" +#include +#include +#include +#include +#include + +InjectAuthlib::InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr* injector) : LaunchStep(parent) +{ + m_injector = injector; +} + +void InjectAuthlib::executeTask() +{ + + if (m_aborted) + { + emitFailed(tr("Task aborted.")); + return; + } + + auto latestVersionInfo = QString("https://authlib-injector.yushi.moe/artifact/latest.json"); + auto netJob = new NetJob("Injector versions info download"); + MetaEntryPtr entry = ENV.metacache()->resolveEntry("injectors", "version.json"); + entry->setStale(true); + auto task = Net::Download::makeCached(QUrl(latestVersionInfo), entry); + netJob->addNetAction(task); + + jobPtr.reset(netJob); + QObject::connect(netJob, &NetJob::succeeded, this, &InjectAuthlib::onVersionDownloadSucceeded); + QObject::connect(netJob, &NetJob::failed, this, &InjectAuthlib::onDownloadFailed); + jobPtr->start(); +} + +void InjectAuthlib::onVersionDownloadSucceeded() +{ + + QByteArray data; + try + { + data = FS::read(QDir("injectors").absoluteFilePath("version.json")); + } + catch (const Exception &e) + { + qCritical() << "Translations Download Failed: index file not readable"; + jobPtr.reset(); + emitFailed("Error while parsing JSON response from InjectorEndpoint"); + return; + } + + QJsonParseError parse_error; + QJsonDocument doc = QJsonDocument::fromJson(data, &parse_error); + if (parse_error.error != QJsonParseError::NoError) + { + qCritical() << "Error while parsing JSON response from InjectorEndpoint at " << parse_error.offset << " reason: " << parse_error.errorString(); + qCritical() << data; + jobPtr.reset(); + emitFailed("Error while parsing JSON response from InjectorEndpoint"); + return; + } + + if (!doc.isObject()) + { + qCritical() << "Error while parsing JSON response from InjectorEndpoint root is not object"; + qCritical() << data; + jobPtr.reset(); + emitFailed("Error while parsing JSON response from InjectorEndpoint"); + return; + } + + QString downloadUrl; + try + { + downloadUrl = Json::requireString(doc.object(), "download_url"); + } + catch (const JSONValidationError &e) + { + qCritical() << "Error while parsing JSON response from InjectorEndpoint download url is not string"; + qCritical() << e.cause(); + qCritical() << data; + jobPtr.reset(); + emitFailed("Error while parsing JSON response from InjectorEndpoint"); + return; + } + + QFileInfo fi(downloadUrl); + m_versionName = fi.fileName(); + + qDebug() << "Authlib injector version:" << m_versionName; + + auto netJob = new NetJob("Injector download"); + MetaEntryPtr entry = ENV.metacache()->resolveEntry("injectors", m_versionName); + entry->setStale(true); + auto task = Net::Download::makeCached(QUrl(downloadUrl), entry); + netJob->addNetAction(task); + + jobPtr.reset(netJob); + QObject::connect(netJob, &NetJob::succeeded, this, &InjectAuthlib::onDownloadSucceeded); + QObject::connect(netJob, &NetJob::failed, this, &InjectAuthlib::onDownloadFailed); + jobPtr->start(); +} + +void InjectAuthlib::onDownloadSucceeded() +{ + QString injector = QString("-javaagent:%1=%2").arg(QDir("injectors").absoluteFilePath(m_versionName)).arg(m_authServer); + + qDebug() + << "Injecting " << injector; + auto inj = new AuthlibInjector(injector); + m_injector->reset(inj); + + jobPtr.reset(); + emitSucceeded(); +} + +void InjectAuthlib::onDownloadFailed(QString reason) +{ + jobPtr.reset(); + emitFailed(reason); +} + +void InjectAuthlib::proceed() +{ +} + +bool InjectAuthlib::canAbort() const +{ + if (jobPtr) + { + return jobPtr->canAbort(); + } + return true; +} + +bool InjectAuthlib::abort() +{ + m_aborted = true; + if (jobPtr) + { + if (jobPtr->canAbort()) + { + return jobPtr->abort(); + } + } + return true; +} diff --git a/api/logic/minecraft/launch/InjectAuthlib.h b/api/logic/minecraft/launch/InjectAuthlib.h new file mode 100644 index 00000000..472e2e1d --- /dev/null +++ b/api/logic/minecraft/launch/InjectAuthlib.h @@ -0,0 +1,70 @@ +/* Copyright 2013-2021 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +struct AuthlibInjector +{ + QString javaArg; + + AuthlibInjector(const QString arg) + { + javaArg = std::move(arg); + qDebug() << "NEW INJECTOR" << javaArg; + } +}; + +typedef std::shared_ptr AuthlibInjectorPtr; + +// FIXME: stupid. should be defined by the instance type? or even completely abstracted away... +class InjectAuthlib : public LaunchStep +{ + Q_OBJECT +public: + InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr *injector); + virtual ~InjectAuthlib(){}; + + void executeTask() override; + bool canAbort() const override; + void proceed() override; + + void setAuthServer(QString server) + { + m_authServer = server; + }; + +public slots: + bool abort() override; + +private slots: + void onVersionDownloadSucceeded(); + void onDownloadSucceeded(); + void onDownloadFailed(QString reason); + +private: + shared_qobject_ptr jobPtr; + bool m_aborted = false; + + QString m_versionName; + QString m_authServer; + AuthlibInjectorPtr *m_injector; +}; diff --git a/api/logic/minecraft/legacy/LegacyInstance.h b/api/logic/minecraft/legacy/LegacyInstance.h index 325bac7a..083531ee 100644 --- a/api/logic/minecraft/legacy/LegacyInstance.h +++ b/api/logic/minecraft/legacy/LegacyInstance.h @@ -112,7 +112,7 @@ public: return false; } shared_qobject_ptr createLaunchTask( - AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override + AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) override { return nullptr; } diff --git a/application/LaunchController.cpp b/application/LaunchController.cpp index 4113a0d3..cd4dde75 100644 --- a/application/LaunchController.cpp +++ b/application/LaunchController.cpp @@ -197,7 +197,7 @@ void LaunchController::launchInstance() return; } - m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin); + m_launcher = m_instance->createLaunchTask(m_session, m_serverToJoin, m_authserver->port()); if (!m_launcher) { emitFailed(tr("Couldn't instantiate a launcher.")); diff --git a/application/LaunchController.h b/application/LaunchController.h index 5f177e00..593ba5e4 100644 --- a/application/LaunchController.h +++ b/application/LaunchController.h @@ -4,6 +4,7 @@ #include #include "minecraft/launch/MinecraftServerTarget.h" +#include "AuthServer.h" class InstanceWindow; class LaunchController: public Task @@ -39,6 +40,10 @@ public: { m_serverToJoin = std::move(serverToJoin); } + void setAuthserver(std::shared_ptr authserver) + { + m_authserver = authserver; + } QString id() { return m_instance->id(); @@ -65,4 +70,5 @@ private: AuthSessionPtr m_session; shared_qobject_ptr m_launcher; MinecraftServerTargetPtr m_serverToJoin; + std::shared_ptr m_authserver; }; diff --git a/application/MultiMC.cpp b/application/MultiMC.cpp index 2dd4f83f..90cf7622 100644 --- a/application/MultiMC.cpp +++ b/application/MultiMC.cpp @@ -54,6 +54,7 @@ #include "tools/JProfiler.h" #include "tools/JVisualVM.h" #include "tools/MCEditTool.h" +#include "AuthServer.h" #include #include "settings/INISettingsObject.h" @@ -724,6 +725,11 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) m_mcedit.reset(new MCEditTool(m_settings)); } + { + m_authserver.reset(new AuthServer(this)); + qDebug() << "<> Auth server started."; + } + connect(this, &MultiMC::aboutToQuit, [this](){ if(m_instances) { @@ -1102,6 +1108,7 @@ bool MultiMC::launch( controller->setOnline(online); controller->setProfiler(profiler); controller->setServerToJoin(serverToJoin); + controller->setAuthserver(m_authserver); if(window) { controller->setParentWidget(window); diff --git a/application/MultiMC.h b/application/MultiMC.h index af2b41c1..1f9e35c1 100644 --- a/application/MultiMC.h +++ b/application/MultiMC.h @@ -34,6 +34,7 @@ class BaseDetachedToolFactory; class TranslationsModel; class ITheme; class MCEditTool; +class AuthServer; class GAnalytics; #if defined(MMC) @@ -194,6 +195,7 @@ private: std::shared_ptr m_globalSettingsProvider; std::map> m_themes; std::unique_ptr m_mcedit; + std::shared_ptr m_authserver; QMap> m_profilers; diff --git a/application/dialogs/LoginDialog.cpp b/application/dialogs/LoginDialog.cpp index dc1fe843..365c807d 100644 --- a/application/dialogs/LoginDialog.cpp +++ b/application/dialogs/LoginDialog.cpp @@ -47,6 +47,8 @@ void LoginDialog::accept() m_account->setLoginType("mojang"); else if (ui->radioDummy->isChecked()) m_account->setLoginType("dummy"); + else if (ui->radioElyby->isChecked()) + m_account->setLoginType("elyby"); m_loginTask = m_account->login(nullptr, ui->passTextBox->text()); connect(m_loginTask.get(), &Task::failed, this, &LoginDialog::onTaskFailed); connect(m_loginTask.get(), &Task::succeeded, this, diff --git a/application/dialogs/LoginDialog.ui b/application/dialogs/LoginDialog.ui index 9ba5f80d..c065c827 100644 --- a/application/dialogs/LoginDialog.ui +++ b/application/dialogs/LoginDialog.ui @@ -81,6 +81,15 @@ false + + + + Ely.by + + + false + + diff --git a/buildconfig/BuildConfig.h b/buildconfig/BuildConfig.h index 9feb7786..b7694cbb 100644 --- a/buildconfig/BuildConfig.h +++ b/buildconfig/BuildConfig.h @@ -68,12 +68,15 @@ public: QString RESOURCE_BASE = "https://resources.download.minecraft.net/"; QString LIBRARY_BASE = "https://libraries.minecraft.net/"; QString SKINS_BASE = "https://crafatar.com/skins/"; - QString AUTH_BASE = "https://authserver.mojang.com/"; QString MOJANG_STATUS_URL = "https://status.mojang.com/check"; QString IMGUR_BASE_URL = "https://api.imgur.com/3/"; QString FMLLIBS_BASE_URL = "https://files.multimc.org/fmllibs/"; QString TRANSLATIONS_BASE_URL = "https://files.multimc.org/translations/"; + + QString AUTH_BASE_MOJANG = "https://authserver.mojang.com/"; + QString AUTH_BASE_ELYBY = "https://authserver.ely.by/auth/"; + QString MODPACKSCH_API_BASE_URL = "https://api.modpacks.ch/"; QString LEGACY_FTB_CDN_BASE_URL = "https://dist.creeper.host/FTB2/";