mirror of
https://github.com/UltimMC/Launcher.git
synced 2025-12-25 04:44:59 +00:00
Fix indent
This commit is contained in:
2
BUILD.md
2
BUILD.md
@@ -12,7 +12,7 @@ Build Instructions
|
||||
# Note
|
||||
|
||||
MultiMC is a portable application and is not supposed to be installed into any system folders.
|
||||
That would be anything outside your home folder. Before running `make install`, make sure
|
||||
That would be anything outside your home folder. Before runing `make install`, make sure
|
||||
you set the install path to something you have write access to. Never build this under
|
||||
an administrator/root level account. Don't use `sudo`. It won't work and it's not supposed to work.
|
||||
|
||||
|
||||
17
README.md
17
README.md
@@ -1,5 +1,5 @@
|
||||
> **This is a "cracked" version of a popular Minecraft launcher that lets you play the game without a Mojang account.**
|
||||
>
|
||||
>
|
||||
> This software is not related to MultiMC developers and provided without any warranty. Please don't bomb MultiMC developers if something gets wrong using this launcher.
|
||||
|
||||
## Pre-built binaries:
|
||||
@@ -9,26 +9,11 @@
|
||||
|
||||
Details about the original launcher below:
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MultiMC 5
|
||||
=========
|
||||
|
||||
MultiMC is a custom launcher for Minecraft that allows you to easily manage multiple installations of Minecraft at once. It also allows you to easily install and remove mods by simply dragging and dropping. Here are the current [features](https://github.com/MultiMC/MultiMC5/wiki#features) of MultiMC.
|
||||
|
||||
## How to get it
|
||||
This project support only cloning, because it has submodules
|
||||
```
|
||||
git clone https://github.com/AfoninZ/MultiMC5-Cracked
|
||||
cd MultiMC5-Cracked
|
||||
git submodule update --init
|
||||
```
|
||||
|
||||
## Development
|
||||
The project uses C++ and Qt5 as the language and base framework. This might seem odd in the Minecraft community, but allows using 25MB of RAM, where other tools use an excessive amount of resources for no reason.
|
||||
|
||||
@@ -5,28 +5,28 @@
|
||||
|
||||
AuthServer::AuthServer(QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_tcpServer.reset(new QTcpServer(this));
|
||||
m_tcpServer.reset(new QTcpServer(this));
|
||||
|
||||
connect(m_tcpServer.get(), &QTcpServer::newConnection, this, &AuthServer::newConnection);
|
||||
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";
|
||||
}
|
||||
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();
|
||||
return m_tcpServer->serverPort();
|
||||
}
|
||||
|
||||
void AuthServer::newConnection()
|
||||
{
|
||||
QTcpSocket *tcpSocket = m_tcpServer->nextPendingConnection();
|
||||
QTcpSocket *tcpSocket = m_tcpServer->nextPendingConnection();
|
||||
|
||||
connect(tcpSocket, &QTcpSocket::readyRead, this, [tcpSocket]()
|
||||
{
|
||||
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");
|
||||
@@ -40,40 +40,40 @@ void AuthServer::newConnection()
|
||||
|
||||
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";
|
||||
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;
|
||||
responseStatusCode = 204;
|
||||
}
|
||||
else
|
||||
{
|
||||
responseBody = "Not found";
|
||||
responseStatusCode = 404;
|
||||
responseBody = "Not found";
|
||||
responseStatusCode = 404;
|
||||
}
|
||||
|
||||
QString responseStatusText = "Internal Server Error";
|
||||
if (responseStatusCode == 200)
|
||||
responseStatusText = "OK";
|
||||
responseStatusText = "OK";
|
||||
else if (responseStatusCode == 204)
|
||||
responseStatusText = "No Content";
|
||||
responseStatusText = "No Content";
|
||||
else if (responseStatusCode == 404)
|
||||
responseStatusText = "Not Found";
|
||||
responseStatusText = "Not Found";
|
||||
|
||||
if (responseBody.length() != 0)
|
||||
{
|
||||
responseHeaders << ((QString) "Content-Length: %1").arg(responseBody.length());
|
||||
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]()
|
||||
{
|
||||
});
|
||||
connect(tcpSocket, &QTcpSocket::disconnected, this, [tcpSocket]()
|
||||
{
|
||||
tcpSocket->close();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@
|
||||
class MULTIMC_LOGIC_EXPORT AuthServer: public QObject
|
||||
{
|
||||
public:
|
||||
explicit AuthServer(QObject *parent = 0);
|
||||
explicit AuthServer(QObject *parent = 0);
|
||||
|
||||
quint16 port();
|
||||
quint16 port();
|
||||
|
||||
private:
|
||||
void newConnection();
|
||||
|
||||
void newConnection();
|
||||
|
||||
private:
|
||||
std::shared_ptr<QTcpServer> m_tcpServer;
|
||||
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, quint16 localAuthServerPort) = 0;
|
||||
AuthSessionPtr account, MinecraftServerTargetPtr serverToJoin, quint16 localAuthServerPort) = 0;
|
||||
|
||||
/// returns the current launch task (if any)
|
||||
shared_qobject_ptr<LaunchTask> getLaunchTask();
|
||||
|
||||
@@ -54,7 +54,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
|
||||
@@ -407,7 +407,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();
|
||||
@@ -487,9 +487,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";
|
||||
}
|
||||
@@ -607,10 +607,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)
|
||||
@@ -747,7 +747,7 @@ 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)"))
|
||||
@@ -816,14 +816,14 @@ 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;
|
||||
}
|
||||
@@ -1061,4 +1061,5 @@ QList< Mod > MinecraftInstance::getJarMods() const
|
||||
return mods;
|
||||
}
|
||||
|
||||
|
||||
#include "MinecraftInstance.moc"
|
||||
|
||||
@@ -20,33 +20,54 @@ typedef std::shared_ptr<BaseAuthProvider> AuthProviderPtr;
|
||||
* provides a standard interface for all providers.
|
||||
*
|
||||
* To create a new provider, create a new class inheriting from this class,
|
||||
* implement the pure virtual functions, and
|
||||
* implement the pure virtual functions, and
|
||||
*/
|
||||
class MULTIMC_LOGIC_EXPORT BaseAuthProvider : public QObject //, public std::enable_shared_from_this<BaseAuthProvider>
|
||||
class MULTIMC_LOGIC_EXPORT BaseAuthProvider : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
virtual ~BaseAuthProvider(){};
|
||||
virtual ~BaseAuthProvider(){};
|
||||
|
||||
// Unique id for provider
|
||||
virtual QString id() { return "base"; };
|
||||
// Unique id for provider
|
||||
virtual QString id()
|
||||
{
|
||||
return "base";
|
||||
};
|
||||
|
||||
// Name of provider that displayed in account selector and list
|
||||
virtual QString displayName() { return "Base"; };
|
||||
// Name of provider that displayed in account selector and list
|
||||
virtual QString displayName()
|
||||
{
|
||||
return "Base";
|
||||
};
|
||||
|
||||
// Use dummy auth on login instead of calling endpoint
|
||||
virtual bool dummyAuth() { return false; };
|
||||
// Use dummy auth on login instead of calling endpoint
|
||||
virtual bool dummyAuth()
|
||||
{
|
||||
return false;
|
||||
};
|
||||
|
||||
// Endpoint for authlib injector (use empty if authlib injector isn't required)
|
||||
virtual QString injectorEndpoint() { return ""; };
|
||||
// Endpoint for authlib injector (use empty if authlib injector isn't required)
|
||||
virtual QString injectorEndpoint()
|
||||
{
|
||||
return "";
|
||||
};
|
||||
|
||||
// Endpoint for authentication
|
||||
virtual QString authEndpoint() { return ""; };
|
||||
// Endpoint for authentication
|
||||
virtual QString authEndpoint()
|
||||
{
|
||||
return "";
|
||||
};
|
||||
|
||||
// Function to get url of skin to display in launcher
|
||||
virtual QUrl resolveSkinUrl(AccountProfile profile) { return QUrl(((QString) "https://crafatar.com/skins/%1.png").arg(profile.id)); };
|
||||
// Function to get url of skin to display in launcher
|
||||
virtual QUrl resolveSkinUrl(AccountProfile profile)
|
||||
{
|
||||
return QUrl(((QString) "https://crafatar.com/skins/%1.png").arg(profile.id));
|
||||
};
|
||||
|
||||
// Can change skin (currently only mojang support)
|
||||
virtual bool canChangeSkin() { return false; }
|
||||
// Can change skin (currently only mojang support)
|
||||
virtual bool canChangeSkin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,15 +11,26 @@
|
||||
|
||||
class DummyAuthProvider : public BaseAuthProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QString id()
|
||||
{
|
||||
return "dummy";
|
||||
}
|
||||
QString id()
|
||||
{
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
QString displayName() { return "Local"; }
|
||||
bool dummyAuth() { return true; }
|
||||
QString injectorEndpoint() { return "http://localhost:%1"; };
|
||||
QString displayName()
|
||||
{
|
||||
return "Local";
|
||||
}
|
||||
|
||||
bool dummyAuth()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
QString injectorEndpoint()
|
||||
{
|
||||
return "http://localhost:%1";
|
||||
};
|
||||
};
|
||||
|
||||
@@ -11,16 +11,31 @@
|
||||
|
||||
class ElybyAuthProvider : public BaseAuthProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QString id()
|
||||
{
|
||||
return "elyby";
|
||||
}
|
||||
QString id()
|
||||
{
|
||||
return "elyby";
|
||||
}
|
||||
|
||||
QString displayName() { return "Ely.by"; };
|
||||
QString injectorEndpoint() { return "ely.by"; };
|
||||
QString authEndpoint() { return "https://authserver.ely.by/auth/"; };
|
||||
QUrl resolveSkinUrl(AccountProfile profile) { return QUrl(((QString) "http://skinsystem.ely.by/skins/%1.png").arg(profile.name)); }
|
||||
QString displayName()
|
||||
{
|
||||
return "Ely.by";
|
||||
};
|
||||
|
||||
QString injectorEndpoint()
|
||||
{
|
||||
return "ely.by";
|
||||
};
|
||||
|
||||
QString authEndpoint()
|
||||
{
|
||||
return "https://authserver.ely.by/auth/";
|
||||
};
|
||||
|
||||
QUrl resolveSkinUrl(AccountProfile profile)
|
||||
{
|
||||
return QUrl(((QString) "http://skinsystem.ely.by/skins/%1.png").arg(profile.name));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -11,15 +11,26 @@
|
||||
|
||||
class MojangAuthProvider : public BaseAuthProvider
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QString id()
|
||||
{
|
||||
return "mojang";
|
||||
}
|
||||
QString id()
|
||||
{
|
||||
return "mojang";
|
||||
}
|
||||
|
||||
QString displayName() { return "Mojang"; };
|
||||
QString authEndpoint() { return "https://authserver.mojang.com/"; };
|
||||
bool canChangeSkin() { return true; };
|
||||
QString displayName()
|
||||
{
|
||||
return "Mojang";
|
||||
};
|
||||
|
||||
QString authEndpoint()
|
||||
{
|
||||
return "https://authserver.mojang.com/";
|
||||
};
|
||||
|
||||
bool canChangeSkin()
|
||||
{
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -22,129 +22,128 @@
|
||||
|
||||
InjectAuthlib::InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr* injector) : LaunchStep(parent)
|
||||
{
|
||||
m_injector = injector;
|
||||
m_injector = injector;
|
||||
}
|
||||
|
||||
void InjectAuthlib::executeTask()
|
||||
{
|
||||
if (m_aborted)
|
||||
{
|
||||
emitFailed(tr("Task aborted."));
|
||||
return;
|
||||
}
|
||||
|
||||
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");
|
||||
if (!m_offlineMode)
|
||||
{
|
||||
entry->setStale(true);
|
||||
auto task = Net::Download::makeCached(QUrl(latestVersionInfo), entry);
|
||||
netJob->addNetAction(task);
|
||||
|
||||
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");
|
||||
if (!m_offlineMode)
|
||||
{
|
||||
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();
|
||||
}
|
||||
else
|
||||
{
|
||||
onVersionDownloadSucceeded();
|
||||
}
|
||||
jobPtr.reset(netJob);
|
||||
QObject::connect(netJob, &NetJob::succeeded, this, &InjectAuthlib::onVersionDownloadSucceeded);
|
||||
QObject::connect(netJob, &NetJob::failed, this, &InjectAuthlib::onDownloadFailed);
|
||||
jobPtr->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
onVersionDownloadSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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();
|
||||
QFileInfo fi(downloadUrl);
|
||||
m_versionName = fi.fileName();
|
||||
|
||||
qDebug() << "Authlib injector version:" << m_versionName;
|
||||
if (!m_offlineMode)
|
||||
{
|
||||
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);
|
||||
qDebug() << "Authlib injector version:" << m_versionName;
|
||||
if (!m_offlineMode)
|
||||
{
|
||||
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();
|
||||
}
|
||||
else
|
||||
{
|
||||
onDownloadSucceeded();
|
||||
}
|
||||
jobPtr.reset(netJob);
|
||||
QObject::connect(netJob, &NetJob::succeeded, this, &InjectAuthlib::onDownloadSucceeded);
|
||||
QObject::connect(netJob, &NetJob::failed, this, &InjectAuthlib::onDownloadFailed);
|
||||
jobPtr->start();
|
||||
}
|
||||
else
|
||||
{
|
||||
onDownloadSucceeded();
|
||||
}
|
||||
}
|
||||
|
||||
void InjectAuthlib::onDownloadSucceeded()
|
||||
{
|
||||
QString injector = QString("-javaagent:%1=%2").arg(QDir("injectors").absoluteFilePath(m_versionName)).arg(m_authServer);
|
||||
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);
|
||||
qDebug()
|
||||
<< "Injecting " << injector;
|
||||
auto inj = new AuthlibInjector(injector);
|
||||
m_injector->reset(inj);
|
||||
|
||||
jobPtr.reset();
|
||||
emitSucceeded();
|
||||
jobPtr.reset();
|
||||
emitSucceeded();
|
||||
}
|
||||
|
||||
void InjectAuthlib::onDownloadFailed(QString reason)
|
||||
{
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
jobPtr.reset();
|
||||
emitFailed(reason);
|
||||
}
|
||||
|
||||
void InjectAuthlib::proceed()
|
||||
@@ -153,22 +152,22 @@ void InjectAuthlib::proceed()
|
||||
|
||||
bool InjectAuthlib::canAbort() const
|
||||
{
|
||||
if (jobPtr)
|
||||
{
|
||||
return jobPtr->canAbort();
|
||||
}
|
||||
return true;
|
||||
if (jobPtr)
|
||||
{
|
||||
return jobPtr->canAbort();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InjectAuthlib::abort()
|
||||
{
|
||||
m_aborted = true;
|
||||
if (jobPtr)
|
||||
{
|
||||
if (jobPtr->canAbort())
|
||||
m_aborted = true;
|
||||
if (jobPtr)
|
||||
{
|
||||
return jobPtr->abort();
|
||||
if (jobPtr->canAbort())
|
||||
{
|
||||
return jobPtr->abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
|
||||
struct AuthlibInjector
|
||||
{
|
||||
QString javaArg;
|
||||
QString javaArg;
|
||||
|
||||
AuthlibInjector(const QString arg)
|
||||
{
|
||||
javaArg = std::move(arg);
|
||||
qDebug() << "NEW INJECTOR" << javaArg;
|
||||
}
|
||||
AuthlibInjector(const QString arg)
|
||||
{
|
||||
javaArg = std::move(arg);
|
||||
qDebug() << "NEW INJECTOR" << javaArg;
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<AuthlibInjector> AuthlibInjectorPtr;
|
||||
@@ -38,38 +38,38 @@ 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
|
||||
Q_OBJECT
|
||||
public:
|
||||
InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr *injector);
|
||||
virtual ~InjectAuthlib(){};
|
||||
InjectAuthlib(LaunchTask *parent, AuthlibInjectorPtr *injector);
|
||||
virtual ~InjectAuthlib(){};
|
||||
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
void proceed() override;
|
||||
void executeTask() override;
|
||||
bool canAbort() const override;
|
||||
void proceed() override;
|
||||
|
||||
void setAuthServer(QString server)
|
||||
{
|
||||
m_authServer = server;
|
||||
};
|
||||
void setAuthServer(QString server)
|
||||
{
|
||||
m_authServer = server;
|
||||
};
|
||||
|
||||
void setOfflineMode(bool offline) {
|
||||
m_offlineMode = offline;
|
||||
}
|
||||
void setOfflineMode(bool offline) {
|
||||
m_offlineMode = offline;
|
||||
}
|
||||
|
||||
public slots:
|
||||
bool abort() override;
|
||||
bool abort() override;
|
||||
|
||||
private slots:
|
||||
void onVersionDownloadSucceeded();
|
||||
void onDownloadSucceeded();
|
||||
void onDownloadFailed(QString reason);
|
||||
void onVersionDownloadSucceeded();
|
||||
void onDownloadSucceeded();
|
||||
void onDownloadFailed(QString reason);
|
||||
|
||||
private:
|
||||
shared_qobject_ptr<Task> jobPtr;
|
||||
bool m_aborted = false;
|
||||
shared_qobject_ptr<Task> jobPtr;
|
||||
bool m_aborted = false;
|
||||
|
||||
bool m_offlineMode;
|
||||
QString m_versionName;
|
||||
QString m_authServer;
|
||||
AuthlibInjectorPtr *m_injector;
|
||||
bool m_offlineMode;
|
||||
QString m_versionName;
|
||||
QString m_authServer;
|
||||
AuthlibInjectorPtr *m_injector;
|
||||
};
|
||||
|
||||
@@ -46,11 +46,11 @@ void LaunchController::login()
|
||||
{
|
||||
// Tell the user they need to log in at least one account in order to play.
|
||||
auto reply = CustomMessageBox::selectable(
|
||||
m_parentWidget, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
||||
"account logged in to MultiMC."
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
|
||||
m_parentWidget, tr("No Accounts"),
|
||||
tr("In order to play Minecraft, you must have at least one Mojang or Minecraft "
|
||||
"account logged in to MultiMC."
|
||||
"Would you like to open the account manager to add an account now?"),
|
||||
QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec();
|
||||
|
||||
if (reply == QMessageBox::Yes)
|
||||
{
|
||||
@@ -117,72 +117,72 @@ void LaunchController::login()
|
||||
}
|
||||
switch (m_session->status)
|
||||
{
|
||||
case AuthSession::Undetermined:
|
||||
case AuthSession::Undetermined:
|
||||
{
|
||||
qCritical() << "Received undetermined session status during login. Bye.";
|
||||
tryagain = false;
|
||||
emitFailed(tr("Received undetermined session status during login."));
|
||||
break;
|
||||
}
|
||||
case AuthSession::RequiresPassword:
|
||||
{
|
||||
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
|
||||
auto username = m_session->username;
|
||||
auto chopN = [](QString toChop, int N) -> QString
|
||||
{
|
||||
qCritical() << "Received undetermined session status during login. Bye.";
|
||||
tryagain = false;
|
||||
emitFailed(tr("Received undetermined session status during login."));
|
||||
break;
|
||||
}
|
||||
case AuthSession::RequiresPassword:
|
||||
{
|
||||
EditAccountDialog passDialog(failReason, m_parentWidget, EditAccountDialog::PasswordField);
|
||||
auto username = m_session->username;
|
||||
auto chopN = [](QString toChop, int N) -> QString
|
||||
if(toChop.size() > N)
|
||||
{
|
||||
if(toChop.size() > N)
|
||||
{
|
||||
auto left = toChop.left(N);
|
||||
left += QString("\u25CF").repeated(toChop.size() - N);
|
||||
return left;
|
||||
}
|
||||
return toChop;
|
||||
};
|
||||
auto left = toChop.left(N);
|
||||
left += QString("\u25CF").repeated(toChop.size() - N);
|
||||
return left;
|
||||
}
|
||||
return toChop;
|
||||
};
|
||||
|
||||
if(username.contains('@'))
|
||||
{
|
||||
auto parts = username.split('@');
|
||||
auto mailbox = chopN(parts[0],3);
|
||||
QString domain = chopN(parts[1], 3);
|
||||
username = mailbox + '@' + domain;
|
||||
}
|
||||
passDialog.setUsername(username);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
password = passDialog.password();
|
||||
}
|
||||
else
|
||||
{
|
||||
tryagain = false;
|
||||
}
|
||||
if(username.contains('@'))
|
||||
{
|
||||
auto parts = username.split('@');
|
||||
auto mailbox = chopN(parts[0],3);
|
||||
QString domain = chopN(parts[1], 3);
|
||||
username = mailbox + '@' + domain;
|
||||
}
|
||||
passDialog.setUsername(username);
|
||||
if (passDialog.exec() == QDialog::Accepted)
|
||||
{
|
||||
password = passDialog.password();
|
||||
}
|
||||
else
|
||||
{
|
||||
tryagain = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AuthSession::PlayableOffline:
|
||||
{
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
QString usedname = m_session->player_name;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal, m_session->player_name, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
case AuthSession::PlayableOffline:
|
||||
if (name.length())
|
||||
{
|
||||
// we ask the user for a player name
|
||||
bool ok = false;
|
||||
QString usedname = m_session->player_name;
|
||||
QString name = QInputDialog::getText(m_parentWidget, tr("Player name"),
|
||||
tr("Choose your offline mode player name."),
|
||||
QLineEdit::Normal, m_session->player_name, &ok);
|
||||
if (!ok)
|
||||
{
|
||||
tryagain = false;
|
||||
break;
|
||||
}
|
||||
if (name.length())
|
||||
{
|
||||
usedname = name;
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
case AuthSession::PlayableOnline:
|
||||
{
|
||||
launchInstance();
|
||||
tryagain = false;
|
||||
return;
|
||||
usedname = name;
|
||||
}
|
||||
m_session->MakeOffline(usedname);
|
||||
// offline flavored game from here :3
|
||||
}
|
||||
case AuthSession::PlayableOnline:
|
||||
{
|
||||
launchInstance();
|
||||
tryagain = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
emitFailed(tr("Failed to launch."));
|
||||
|
||||
Reference in New Issue
Block a user