mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-26 10:35:15 +00:00
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
This commit is contained in:
79
api/logic/AuthServer.cpp
Normal file
79
api/logic/AuthServer.cpp
Normal file
@@ -0,0 +1,79 @@
|
||||
#include "AuthServer.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QTcpSocket>
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
20
api/logic/AuthServer.h
Normal file
20
api/logic/AuthServer.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QTcpServer>
|
||||
#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<QTcpServer> m_tcpServer;
|
||||
};
|
||||
@@ -148,7 +148,7 @@ public:
|
||||
|
||||
/// returns a valid launcher (task container)
|
||||
virtual shared_qobject_ptr<LaunchTask> createLaunchTask(
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) = 0;
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) = 0;
|
||||
|
||||
/// returns the current launch task (if any)
|
||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
{
|
||||
return instanceRoot();
|
||||
};
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr) override
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr, MinecraftServerTargetPtr, quint16) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -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<Setting> a, std::shared_ptr<Setting> 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<QString, QString> 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<Task> MinecraftInstance::createUpdateTask(Net::Mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case Net::Mode::Offline:
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
|
||||
}
|
||||
case Net::Mode::Online:
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new MinecraftUpdate(this));
|
||||
}
|
||||
case Net::Mode::Offline:
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new MinecraftLoadAndCheck(this));
|
||||
}
|
||||
case Net::Mode::Online:
|
||||
{
|
||||
return shared_qobject_ptr<Task>(new MinecraftUpdate(this));
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin)
|
||||
shared_qobject_ptr<LaunchTask> MinecraftInstance::createLaunchTask(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort)
|
||||
{
|
||||
// FIXME: get rid of shared_from_this ...
|
||||
auto process = LaunchTask::create(std::dynamic_pointer_cast<MinecraftInstance>(shared_from_this()));
|
||||
@@ -914,6 +917,17 @@ shared_qobject_ptr<LaunchTask> 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();
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <QDir>
|
||||
#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<Task> createUpdateTask(Net::Mode mode) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override;
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) override;
|
||||
QStringList extraArguments() const override;
|
||||
QStringList verboseDescription(AuthSessionPtr session, MinecraftServerTargetPtr serverToJoin) override;
|
||||
QList<Mod> getJarMods() const;
|
||||
@@ -128,6 +129,7 @@ protected: // data
|
||||
mutable std::shared_ptr<ModFolderModel> m_texture_pack_list;
|
||||
mutable std::shared_ptr<WorldList> m_world_list;
|
||||
mutable std::shared_ptr<GameOptions> m_game_options;
|
||||
mutable std::shared_ptr<AuthlibInjector> m_injector;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<MinecraftInstance> MinecraftInstancePtr;
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
#include <BuildConfig.h>
|
||||
|
||||
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<YggdrasilTask> MojangAccount::login(AuthSessionPtr session, QString password)
|
||||
{
|
||||
Q_ASSERT(m_currentTask.get() == nullptr);
|
||||
@@ -202,7 +212,7 @@ std::shared_ptr<YggdrasilTask> 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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
|
||||
#include <Env.h>
|
||||
|
||||
#include <BuildConfig.h>
|
||||
|
||||
#include <QDebug>
|
||||
|
||||
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();
|
||||
|
||||
161
api/logic/minecraft/launch/InjectAuthlib.cpp
Normal file
161
api/logic/minecraft/launch/InjectAuthlib.cpp
Normal file
@@ -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 <launch/LaunchTask.h>
|
||||
#include <minecraft/MinecraftInstance.h>
|
||||
#include <FileSystem.h>
|
||||
#include <Env.h>
|
||||
#include <Json.h>
|
||||
|
||||
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;
|
||||
}
|
||||
70
api/logic/minecraft/launch/InjectAuthlib.h
Normal file
70
api/logic/minecraft/launch/InjectAuthlib.h
Normal file
@@ -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 <launch/LaunchStep.h>
|
||||
#include <QObjectPtr.h>
|
||||
#include <LoggedProcess.h>
|
||||
#include <java/JavaChecker.h>
|
||||
#include <net/Mode.h>
|
||||
#include <net/NetJob.h>
|
||||
|
||||
struct AuthlibInjector
|
||||
{
|
||||
QString javaArg;
|
||||
|
||||
AuthlibInjector(const QString arg)
|
||||
{
|
||||
javaArg = std::move(arg);
|
||||
qDebug() << "NEW INJECTOR" << javaArg;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<AuthlibInjector> 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<Task> jobPtr;
|
||||
bool m_aborted = false;
|
||||
|
||||
QString m_versionName;
|
||||
QString m_authServer;
|
||||
AuthlibInjectorPtr *m_injector;
|
||||
};
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
shared_qobject_ptr<LaunchTask> createLaunchTask(
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin) override
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user