mirror of
https://github.com/PrismLauncher/PrismLauncher.git
synced 2025-06-13 05:37:42 +02:00
Merge branch 'develop' of https://github.com/PrismLauncher/PrismLauncher into metadata2
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
This commit is contained in:
@ -42,6 +42,20 @@
|
||||
#include <net/ApiDownload.h>
|
||||
#include <net/ChecksumValidator.h>
|
||||
|
||||
/**
|
||||
* @brief Collect applicable files for the library.
|
||||
*
|
||||
* Depending on whether the library is native or not, it adds paths to the
|
||||
* appropriate lists for jar files, native libraries for 32-bit, and native
|
||||
* libraries for 64-bit.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @param jar List to store paths for jar files.
|
||||
* @param native List to store paths for native libraries.
|
||||
* @param native32 List to store paths for 32-bit native libraries.
|
||||
* @param native64 List to store paths for 64-bit native libraries.
|
||||
* @param overridePath Optional path to override the default storage path.
|
||||
*/
|
||||
void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||
QStringList& jar,
|
||||
QStringList& native,
|
||||
@ -50,6 +64,7 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||
const QString& overridePath) const
|
||||
{
|
||||
bool local = isLocal();
|
||||
// Lambda function to get the absolute file path
|
||||
auto actualPath = [&](QString relPath) {
|
||||
relPath = FS::RemoveInvalidPathChars(relPath);
|
||||
QFileInfo out(FS::PathCombine(storagePrefix(), relPath));
|
||||
@ -59,6 +74,7 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||
}
|
||||
return out.absoluteFilePath();
|
||||
};
|
||||
|
||||
QString raw_storage = storageSuffix(runtimeContext);
|
||||
if (isNative()) {
|
||||
if (raw_storage.contains("${arch}")) {
|
||||
@ -76,6 +92,19 @@ void Library::getApplicableFiles(const RuntimeContext& runtimeContext,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get download requests for the library files.
|
||||
*
|
||||
* Depending on whether the library is native or not, and the current runtime context,
|
||||
* this function prepares download requests for the necessary files. It handles both local
|
||||
* and remote files, checks for stale cache entries, and adds checksummed downloads.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @param cache Pointer to the HTTP meta cache.
|
||||
* @param failedLocalFiles List to store paths for failed local files.
|
||||
* @param overridePath Optional path to override the default storage path.
|
||||
* @return QList<Net::NetRequest::Ptr> List of download requests.
|
||||
*/
|
||||
QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeContext,
|
||||
class HttpMetaCache* cache,
|
||||
QStringList& failedLocalFiles,
|
||||
@ -85,6 +114,7 @@ QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeC
|
||||
bool stale = isAlwaysStale();
|
||||
bool local = isLocal();
|
||||
|
||||
// Lambda function to check if a local file exists
|
||||
auto check_local_file = [&](QString storage) {
|
||||
QFileInfo fileinfo(storage);
|
||||
QString fileName = fileinfo.fileName();
|
||||
@ -97,6 +127,7 @@ QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeC
|
||||
return true;
|
||||
};
|
||||
|
||||
// Lambda function to add a download request
|
||||
auto add_download = [&](QString storage, QString url, QString sha1) {
|
||||
if (local) {
|
||||
return check_local_file(storage);
|
||||
@ -197,6 +228,15 @@ QList<Net::NetRequest::Ptr> Library::getDownloads(const RuntimeContext& runtimeC
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the library is active in the given runtime context.
|
||||
*
|
||||
* This function evaluates rules to determine if the library should be active,
|
||||
* considering both general rules and native compatibility.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @return bool True if the library is active, false otherwise.
|
||||
*/
|
||||
bool Library::isActive(const RuntimeContext& runtimeContext) const
|
||||
{
|
||||
bool result = true;
|
||||
@ -217,16 +257,35 @@ bool Library::isActive(const RuntimeContext& runtimeContext) const
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the library is considered local.
|
||||
*
|
||||
* @return bool True if the library is local, false otherwise.
|
||||
*/
|
||||
bool Library::isLocal() const
|
||||
{
|
||||
return m_hint == "local";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the library is always considered stale.
|
||||
*
|
||||
* @return bool True if the library is always stale, false otherwise.
|
||||
*/
|
||||
bool Library::isAlwaysStale() const
|
||||
{
|
||||
return m_hint == "always-stale";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the compatible native classifier for the current runtime context.
|
||||
*
|
||||
* This function attempts to match the current runtime context with the appropriate
|
||||
* native classifier.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @return QString The compatible native classifier, or an empty string if none is found.
|
||||
*/
|
||||
QString Library::getCompatibleNative(const RuntimeContext& runtimeContext) const
|
||||
{
|
||||
// try to match precise classifier "[os]-[arch]"
|
||||
@ -241,16 +300,31 @@ QString Library::getCompatibleNative(const RuntimeContext& runtimeContext) const
|
||||
return entry.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the storage prefix for the library.
|
||||
*
|
||||
* @param prefix The storage prefix to set.
|
||||
*/
|
||||
void Library::setStoragePrefix(QString prefix)
|
||||
{
|
||||
m_storagePrefix = prefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the default storage prefix for libraries.
|
||||
*
|
||||
* @return QString The default storage prefix.
|
||||
*/
|
||||
QString Library::defaultStoragePrefix()
|
||||
{
|
||||
return "libraries/";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current storage prefix for the library.
|
||||
*
|
||||
* @return QString The current storage prefix.
|
||||
*/
|
||||
QString Library::storagePrefix() const
|
||||
{
|
||||
if (m_storagePrefix.isEmpty()) {
|
||||
@ -259,6 +333,15 @@ QString Library::storagePrefix() const
|
||||
return m_storagePrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the filename for the library in the current runtime context.
|
||||
*
|
||||
* This function determines the appropriate filename for the library, taking into
|
||||
* account native classifiers if applicable.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @return QString The filename of the library.
|
||||
*/
|
||||
QString Library::filename(const RuntimeContext& runtimeContext) const
|
||||
{
|
||||
if (!m_filename.isEmpty()) {
|
||||
@ -280,6 +363,15 @@ QString Library::filename(const RuntimeContext& runtimeContext) const
|
||||
return nativeSpec.getFileName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the display name for the library in the current runtime context.
|
||||
*
|
||||
* This function returns the display name for the library, defaulting to the filename
|
||||
* if no display name is set.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @return QString The display name of the library.
|
||||
*/
|
||||
QString Library::displayName(const RuntimeContext& runtimeContext) const
|
||||
{
|
||||
if (!m_displayname.isEmpty())
|
||||
@ -287,6 +379,15 @@ QString Library::displayName(const RuntimeContext& runtimeContext) const
|
||||
return filename(runtimeContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the storage suffix for the library in the current runtime context.
|
||||
*
|
||||
* This function determines the appropriate storage suffix for the library, taking into
|
||||
* account native classifiers if applicable.
|
||||
*
|
||||
* @param runtimeContext The current runtime context.
|
||||
* @return QString The storage suffix of the library.
|
||||
*/
|
||||
QString Library::storageSuffix(const RuntimeContext& runtimeContext) const
|
||||
{
|
||||
// non-native? use only the gradle specifier
|
||||
|
@ -58,7 +58,6 @@
|
||||
#include "ComponentUpdateTask.h"
|
||||
#include "PackProfile.h"
|
||||
#include "PackProfile_p.h"
|
||||
#include "minecraft/mod/Mod.h"
|
||||
#include "modplatform/ModIndex.h"
|
||||
|
||||
static const QMap<QString, ModPlatform::ModLoaderType> modloaderMapping{ { "net.neoforged", ModPlatform::NeoForge },
|
||||
@ -181,7 +180,7 @@ static bool loadPackProfile(PackProfile* parent,
|
||||
}
|
||||
if (!componentsFile.open(QFile::ReadOnly)) {
|
||||
qCritical() << "Couldn't open" << componentsFile.fileName() << " for reading:" << componentsFile.errorString();
|
||||
qWarning() << "Ignoring overriden order";
|
||||
qWarning() << "Ignoring overridden order";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -190,7 +189,7 @@ static bool loadPackProfile(PackProfile* parent,
|
||||
QJsonDocument doc = QJsonDocument::fromJson(componentsFile.readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCritical() << "Couldn't parse" << componentsFile.fileName() << ":" << error.errorString();
|
||||
qWarning() << "Ignoring overriden order";
|
||||
qWarning() << "Ignoring overridden order";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1022,3 +1021,23 @@ std::optional<ModPlatform::ModLoaderTypes> PackProfile::getSupportedModLoaders()
|
||||
loaders |= ModPlatform::Forge;
|
||||
return loaders;
|
||||
}
|
||||
|
||||
QList<ModPlatform::ModLoaderType> PackProfile::getModLoadersList()
|
||||
{
|
||||
QList<ModPlatform::ModLoaderType> result;
|
||||
for (auto c : d->components) {
|
||||
if (c->isEnabled() && modloaderMapping.contains(c->getID())) {
|
||||
result.append(modloaderMapping[c->getID()]);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this or add version condition once Quilt drops official Fabric support
|
||||
if (result.contains(ModPlatform::Quilt) && !result.contains(ModPlatform::Fabric)) {
|
||||
result.append(ModPlatform::Fabric);
|
||||
}
|
||||
if (getComponentVersion("net.minecraft") == "1.20.1" && result.contains(ModPlatform::NeoForge) &&
|
||||
!result.contains(ModPlatform::Forge)) {
|
||||
result.append(ModPlatform::Forge);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -146,6 +146,7 @@ class PackProfile : public QAbstractListModel {
|
||||
std::optional<ModPlatform::ModLoaderTypes> getModLoaders();
|
||||
// this returns aditional loaders(Quilt supports fabric and NeoForge supports Forge)
|
||||
std::optional<ModPlatform::ModLoaderTypes> getSupportedModLoaders();
|
||||
QList<ModPlatform::ModLoaderType> getModLoadersList();
|
||||
|
||||
private:
|
||||
void scheduleSave();
|
||||
|
@ -57,7 +57,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
|
||||
}
|
||||
if (!orderFile.open(QFile::ReadOnly)) {
|
||||
qCritical() << "Couldn't open" << orderFile.fileName() << " for reading:" << orderFile.errorString();
|
||||
qWarning() << "Ignoring overriden order";
|
||||
qWarning() << "Ignoring overridden order";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
|
||||
QJsonDocument doc = QJsonDocument::fromJson(orderFile.readAll(), &error);
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
qCritical() << "Couldn't parse" << orderFile.fileName() << ":" << error.errorString();
|
||||
qWarning() << "Ignoring overriden order";
|
||||
qWarning() << "Ignoring overridden order";
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ bool readOverrideOrders(QString path, PatchOrder& order)
|
||||
}
|
||||
} catch ([[maybe_unused]] const JSONValidationError& err) {
|
||||
qCritical() << "Couldn't parse" << orderFile.fileName() << ": bad file format";
|
||||
qWarning() << "Ignoring overriden order";
|
||||
qWarning() << "Ignoring overridden order";
|
||||
order.clear();
|
||||
return false;
|
||||
}
|
||||
|
@ -59,6 +59,9 @@ void AuthFlow::executeTask()
|
||||
|
||||
void AuthFlow::nextStep()
|
||||
{
|
||||
if (!Task::isRunning()) {
|
||||
return;
|
||||
}
|
||||
if (m_steps.size() == 0) {
|
||||
// we got to the end without an incident... assume this is all.
|
||||
m_currentStep.reset();
|
||||
@ -143,4 +146,11 @@ bool AuthFlow::changeState(AccountTaskState newState, QString reason)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool AuthFlow::abort()
|
||||
{
|
||||
emitAborted();
|
||||
if (m_currentStep)
|
||||
m_currentStep->abort();
|
||||
return true;
|
||||
}
|
@ -24,6 +24,9 @@ class AuthFlow : public Task {
|
||||
|
||||
AccountTaskState taskState() { return m_taskState; }
|
||||
|
||||
public slots:
|
||||
bool abort() override;
|
||||
|
||||
signals:
|
||||
void authorizeWithBrowser(const QUrl& url);
|
||||
void authorizeWithBrowserWithExtra(QString url, QString code, int expiresIn);
|
||||
|
@ -34,6 +34,7 @@ class AuthStep : public QObject {
|
||||
|
||||
public slots:
|
||||
virtual void perform() = 0;
|
||||
virtual void abort() {}
|
||||
|
||||
signals:
|
||||
void finished(AccountTaskState resultingState, QString message);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "Logging.h"
|
||||
#include "minecraft/auth/Parsers.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/StaticHeaderProxy.h"
|
||||
#include "tasks/Task.h"
|
||||
|
||||
@ -31,12 +32,15 @@ void EntitlementsStep::perform()
|
||||
{ "Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8() } };
|
||||
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Download::makeByteArray(url, m_response);
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Download::makeByteArray(url, m_response);
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("EntitlementsStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &EntitlementsStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
qDebug() << "Getting entitlements...";
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
class EntitlementsStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
@ -22,5 +23,6 @@ class EntitlementsStep : public AuthStep {
|
||||
private:
|
||||
QString m_entitlements_request_id;
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Download::Ptr m_task;
|
||||
Net::Download::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -17,17 +17,20 @@ void GetSkinStep::perform()
|
||||
QUrl url(m_data->minecraftProfile.skin.url);
|
||||
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Download::makeByteArray(url, m_response);
|
||||
m_request = Net::Download::makeByteArray(url, m_response);
|
||||
|
||||
m_task.reset(new NetJob("GetSkinStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &GetSkinStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void GetSkinStep::onRequestDone()
|
||||
{
|
||||
if (m_task->error() == QNetworkReply::NoError)
|
||||
if (m_request->error() == QNetworkReply::NoError)
|
||||
m_data->minecraftProfile.skin.data = *m_response;
|
||||
emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Got skin"));
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got skin"));
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
class GetSkinStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
@ -21,5 +22,6 @@ class GetSkinStep : public AuthStep {
|
||||
|
||||
private:
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Download::Ptr m_task;
|
||||
Net::Download::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -37,12 +37,15 @@ void LauncherLoginStep::perform()
|
||||
};
|
||||
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8());
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Upload::makeByteArray(url, m_response, requestBody.toUtf8());
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("LauncherLoginStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &LauncherLoginStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
qDebug() << "Getting Minecraft access token...";
|
||||
}
|
||||
@ -50,12 +53,13 @@ void LauncherLoginStep::perform()
|
||||
void LauncherLoginStep::onRequestDone()
|
||||
{
|
||||
qCDebug(authCredentials()) << *m_response;
|
||||
if (m_task->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_task->error();
|
||||
if (Net::isApplicationError(m_task->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to get Minecraft access token: %1").arg(m_task->errorString()));
|
||||
if (m_request->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_request->error();
|
||||
if (Net::isApplicationError(m_request->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||
tr("Failed to get Minecraft access token: %1").arg(m_request->errorString()));
|
||||
} else {
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(m_task->errorString()));
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to get Minecraft access token: %1").arg(m_request->errorString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/Upload.h"
|
||||
|
||||
class LauncherLoginStep : public AuthStep {
|
||||
@ -21,5 +22,6 @@ class LauncherLoginStep : public AuthStep {
|
||||
|
||||
private:
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Upload::Ptr m_task;
|
||||
Net::Upload::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -46,6 +46,8 @@
|
||||
MSADeviceCodeStep::MSADeviceCodeStep(AccountData* data) : AuthStep(data)
|
||||
{
|
||||
m_clientId = APPLICATION->getMSAClientID();
|
||||
connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort);
|
||||
connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser);
|
||||
}
|
||||
|
||||
QString MSADeviceCodeStep::describe()
|
||||
@ -65,12 +67,15 @@ void MSADeviceCodeStep::perform()
|
||||
{ "Accept", "application/json" },
|
||||
};
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Upload::makeByteArray(url, m_response, payload);
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Upload::makeByteArray(url, m_response, payload);
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("MSADeviceCodeStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::deviceAutorizationFinished);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
@ -115,7 +120,7 @@ void MSADeviceCodeStep::deviceAutorizationFinished()
|
||||
tr("Device authorization failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description));
|
||||
return;
|
||||
}
|
||||
if (!m_task->wasSuccessful() || m_task->error() != QNetworkReply::NoError) {
|
||||
if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Failed to retrieve device authorization"));
|
||||
qDebug() << *m_response;
|
||||
return;
|
||||
@ -133,9 +138,7 @@ void MSADeviceCodeStep::deviceAutorizationFinished()
|
||||
m_expiration_timer.setTimerType(Qt::VeryCoarseTimer);
|
||||
m_expiration_timer.setInterval(rsp.expires_in * 1000);
|
||||
m_expiration_timer.setSingleShot(true);
|
||||
connect(&m_expiration_timer, &QTimer::timeout, this, &MSADeviceCodeStep::abort);
|
||||
m_expiration_timer.start();
|
||||
|
||||
m_pool_timer.setTimerType(Qt::VeryCoarseTimer);
|
||||
m_pool_timer.setSingleShot(true);
|
||||
startPoolTimer();
|
||||
@ -145,8 +148,8 @@ void MSADeviceCodeStep::abort()
|
||||
{
|
||||
m_expiration_timer.stop();
|
||||
m_pool_timer.stop();
|
||||
if (m_task) {
|
||||
m_task->abort();
|
||||
if (m_request) {
|
||||
m_request->abort();
|
||||
}
|
||||
m_is_aborted = true;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Task aborted"));
|
||||
@ -157,8 +160,12 @@ void MSADeviceCodeStep::startPoolTimer()
|
||||
if (m_is_aborted) {
|
||||
return;
|
||||
}
|
||||
if (m_expiration_timer.remainingTime() < interval * 1000) {
|
||||
perform();
|
||||
return;
|
||||
}
|
||||
|
||||
m_pool_timer.setInterval(interval * 1000);
|
||||
connect(&m_pool_timer, &QTimer::timeout, this, &MSADeviceCodeStep::authenticateUser);
|
||||
m_pool_timer.start();
|
||||
}
|
||||
|
||||
@ -175,13 +182,13 @@ void MSADeviceCodeStep::authenticateUser()
|
||||
{ "Accept", "application/json" },
|
||||
};
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Upload::makeByteArray(url, m_response, payload);
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Upload::makeByteArray(url, m_response, payload);
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &MSADeviceCodeStep::authenticationFinished);
|
||||
connect(m_request.get(), &Task::finished, this, &MSADeviceCodeStep::authenticationFinished);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
m_request->setNetwork(APPLICATION->network());
|
||||
m_request->start();
|
||||
}
|
||||
|
||||
struct AuthenticationResponse {
|
||||
@ -221,7 +228,7 @@ AuthenticationResponse parseAuthenticationResponse(const QByteArray& data)
|
||||
|
||||
void MSADeviceCodeStep::authenticationFinished()
|
||||
{
|
||||
if (m_task->error() == QNetworkReply::TimeoutError) {
|
||||
if (m_request->error() == QNetworkReply::TimeoutError) {
|
||||
// rfc8628#section-3.5
|
||||
// "On encountering a connection timeout, clients MUST unilaterally
|
||||
// reduce their polling frequency before retrying. The use of an
|
||||
@ -254,7 +261,7 @@ void MSADeviceCodeStep::authenticationFinished()
|
||||
tr("Device Access failed: %1").arg(rsp.error_description.isEmpty() ? rsp.error : rsp.error_description));
|
||||
return;
|
||||
}
|
||||
if (!m_task->wasSuccessful() || m_task->error() != QNetworkReply::NoError) {
|
||||
if (!m_request->wasSuccessful() || m_request->error() != QNetworkReply::NoError) {
|
||||
startPoolTimer(); // it failed so just try again without increasing the interval
|
||||
return;
|
||||
}
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include <QTimer>
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/Upload.h"
|
||||
|
||||
class MSADeviceCodeStep : public AuthStep {
|
||||
@ -51,7 +52,7 @@ class MSADeviceCodeStep : public AuthStep {
|
||||
QString describe() override;
|
||||
|
||||
public slots:
|
||||
void abort();
|
||||
void abort() override;
|
||||
|
||||
signals:
|
||||
void authorizeWithBrowser(QString url, QString code, int expiresIn);
|
||||
@ -72,5 +73,6 @@ class MSADeviceCodeStep : public AuthStep {
|
||||
QTimer m_expiration_timer;
|
||||
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Upload::Ptr m_task;
|
||||
Net::Upload::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -35,22 +35,74 @@
|
||||
|
||||
#include "MSAStep.h"
|
||||
|
||||
#include <QtNetworkAuth/qoauthhttpserverreplyhandler.h>
|
||||
#include <QAbstractOAuth2>
|
||||
#include <QNetworkRequest>
|
||||
#include <QOAuthHttpServerReplyHandler>
|
||||
#include <QOAuthOobReplyHandler>
|
||||
|
||||
#include "Application.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "FileSystem.h"
|
||||
|
||||
#include <QProcess>
|
||||
#include <QSettings>
|
||||
#include <QStandardPaths>
|
||||
|
||||
bool isSchemeHandlerRegistered()
|
||||
{
|
||||
#ifdef Q_OS_LINUX
|
||||
QProcess process;
|
||||
process.start("xdg-mime", { "query", "default", "x-scheme-handler/" + BuildConfig.LAUNCHER_APP_BINARY_NAME });
|
||||
process.waitForFinished();
|
||||
QString output = process.readAllStandardOutput().trimmed();
|
||||
|
||||
return output.contains(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||
|
||||
#elif defined(Q_OS_WIN)
|
||||
QString regPath = QString("HKEY_CURRENT_USER\\Software\\Classes\\%1").arg(BuildConfig.LAUNCHER_APP_BINARY_NAME);
|
||||
QSettings settings(regPath, QSettings::NativeFormat);
|
||||
|
||||
return settings.contains("shell/open/command/.");
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
class CustomOAuthOobReplyHandler : public QOAuthOobReplyHandler {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomOAuthOobReplyHandler(QObject* parent = nullptr) : QOAuthOobReplyHandler(parent)
|
||||
{
|
||||
connect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived);
|
||||
}
|
||||
~CustomOAuthOobReplyHandler() override
|
||||
{
|
||||
disconnect(APPLICATION, &Application::oauthReplyRecieved, this, &QOAuthOobReplyHandler::callbackReceived);
|
||||
}
|
||||
QString callback() const override { return BuildConfig.LAUNCHER_APP_BINARY_NAME + "://oauth/microsoft"; }
|
||||
};
|
||||
|
||||
MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(silent)
|
||||
{
|
||||
m_clientId = APPLICATION->getMSAClientID();
|
||||
if (QCoreApplication::applicationFilePath().startsWith("/tmp/.mount_") ||
|
||||
QFile::exists(FS::PathCombine(APPLICATION->root(), "portable.txt")) || !isSchemeHandlerRegistered())
|
||||
|
||||
auto replyHandler = new QOAuthHttpServerReplyHandler(1337, this);
|
||||
replyHandler->setCallbackText(
|
||||
" <iframe src=\"https://prismlauncher.org/successful-login\" title=\"PrismLauncher Microsoft login\" style=\"position:fixed; "
|
||||
"top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; "
|
||||
"z-index:999999;\"/> ");
|
||||
oauth2.setReplyHandler(replyHandler);
|
||||
{
|
||||
auto replyHandler = new QOAuthHttpServerReplyHandler(this);
|
||||
replyHandler->setCallbackText(R"XXX(
|
||||
<noscript>
|
||||
<meta http-equiv="Refresh" content="0; URL=https://prismlauncher.org/successful-login" />
|
||||
</noscript>
|
||||
Login Successful, redirecting...
|
||||
<script>
|
||||
window.location.replace("https://prismlauncher.org/successful-login");
|
||||
</script>
|
||||
)XXX");
|
||||
oauth2.setReplyHandler(replyHandler);
|
||||
} else {
|
||||
oauth2.setReplyHandler(new CustomOAuthOobReplyHandler(this));
|
||||
}
|
||||
oauth2.setAuthorizationUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"));
|
||||
oauth2.setAccessTokenUrl(QUrl("https://login.microsoftonline.com/consumers/oauth2/v2.0/token"));
|
||||
oauth2.setScope("XboxLive.SignIn XboxLive.offline_access");
|
||||
@ -67,9 +119,27 @@ MSAStep::MSAStep(AccountData* data, bool silent) : AuthStep(data), m_silent(sile
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Got "));
|
||||
});
|
||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, this, &MSAStep::authorizeWithBrowser);
|
||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this](const QAbstractOAuth2::Error err) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, tr("Microsoft user authentication failed."));
|
||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::requestFailed, this, [this, silent](const QAbstractOAuth2::Error err) {
|
||||
auto state = AccountTaskState::STATE_FAILED_HARD;
|
||||
if (oauth2.status() == QAbstractOAuth::Status::Granted || silent) {
|
||||
if (err == QAbstractOAuth2::Error::NetworkError) {
|
||||
state = AccountTaskState::STATE_OFFLINE;
|
||||
} else {
|
||||
state = AccountTaskState::STATE_FAILED_SOFT;
|
||||
}
|
||||
}
|
||||
auto message = tr("Microsoft user authentication failed.");
|
||||
if (silent) {
|
||||
message = tr("Failed to refresh token.");
|
||||
}
|
||||
qWarning() << message;
|
||||
emit finished(state, message);
|
||||
});
|
||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::error, this,
|
||||
[this](const QString& error, const QString& errorDescription, const QUrl& uri) {
|
||||
qWarning() << "Failed to login because" << error << errorDescription;
|
||||
emit finished(AccountTaskState::STATE_FAILED_HARD, errorDescription);
|
||||
});
|
||||
|
||||
connect(&oauth2, &QOAuth2AuthorizationCodeFlow::extraTokensChanged, this,
|
||||
[this](const QVariantMap& tokens) { m_data->msaToken.extra = tokens; });
|
||||
@ -89,20 +159,27 @@ void MSAStep::perform()
|
||||
if (m_data->msaClientID != m_clientId) {
|
||||
emit finished(AccountTaskState::STATE_DISABLED,
|
||||
tr("Microsoft user authentication failed - client identification has changed."));
|
||||
return;
|
||||
}
|
||||
if (m_data->msaToken.refresh_token.isEmpty()) {
|
||||
emit finished(AccountTaskState::STATE_DISABLED, tr("Microsoft user authentication failed - refresh token is empty."));
|
||||
return;
|
||||
}
|
||||
oauth2.setRefreshToken(m_data->msaToken.refresh_token);
|
||||
oauth2.refreshAccessToken();
|
||||
} else {
|
||||
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) // QMultiMap param changed in 6.0
|
||||
oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* map) {
|
||||
oauth2.setModifyParametersFunction(
|
||||
[](QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
||||
#else
|
||||
oauth2.setModifyParametersFunction([](QAbstractOAuth::Stage stage, QMap<QString, QVariant>* map) {
|
||||
oauth2.setModifyParametersFunction(
|
||||
[](QAbstractOAuth::Stage stage, QMap<QString, QVariant>* map) { map->insert("prompt", "select_account"); });
|
||||
#endif
|
||||
map->insert("prompt", "select_account");
|
||||
});
|
||||
|
||||
*m_data = AccountData();
|
||||
m_data->msaClientID = m_clientId;
|
||||
oauth2.grant();
|
||||
}
|
||||
}
|
||||
|
||||
#include "MSAStep.moc"
|
@ -22,37 +22,41 @@ void MinecraftProfileStep::perform()
|
||||
{ "Authorization", QString("Bearer %1").arg(m_data->yggdrasilToken.token).toUtf8() } };
|
||||
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Download::makeByteArray(url, m_response);
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Download::makeByteArray(url, m_response);
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("MinecraftProfileStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &MinecraftProfileStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
}
|
||||
|
||||
void MinecraftProfileStep::onRequestDone()
|
||||
{
|
||||
if (m_task->error() == QNetworkReply::ContentNotFoundError) {
|
||||
if (m_request->error() == QNetworkReply::ContentNotFoundError) {
|
||||
// NOTE: Succeed even if we do not have a profile. This is a valid account state.
|
||||
m_data->minecraftProfile = MinecraftProfile();
|
||||
emit finished(AccountTaskState::STATE_SUCCEEDED, tr("Account has no Minecraft profile."));
|
||||
emit finished(AccountTaskState::STATE_WORKING, tr("Account has no Minecraft profile."));
|
||||
return;
|
||||
}
|
||||
if (m_task->error() != QNetworkReply::NoError) {
|
||||
if (m_request->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Error getting profile:";
|
||||
qWarning() << " HTTP Status: " << m_task->replyStatusCode();
|
||||
qWarning() << " Internal error no.: " << m_task->error();
|
||||
qWarning() << " Error string: " << m_task->errorString();
|
||||
qWarning() << " HTTP Status: " << m_request->replyStatusCode();
|
||||
qWarning() << " Internal error no.: " << m_request->error();
|
||||
qWarning() << " Error string: " << m_request->errorString();
|
||||
|
||||
qWarning() << " Response:";
|
||||
qWarning() << QString::fromUtf8(*m_response);
|
||||
|
||||
if (Net::isApplicationError(m_task->error())) {
|
||||
if (Net::isApplicationError(m_request->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||
tr("Minecraft Java profile acquisition failed: %1").arg(m_task->errorString()));
|
||||
tr("Minecraft Java profile acquisition failed: %1").arg(m_request->errorString()));
|
||||
} else {
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("Minecraft Java profile acquisition failed: %1").arg(m_task->errorString()));
|
||||
emit finished(AccountTaskState::STATE_OFFLINE,
|
||||
tr("Minecraft Java profile acquisition failed: %1").arg(m_request->errorString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
class MinecraftProfileStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
@ -21,5 +22,6 @@ class MinecraftProfileStep : public AuthStep {
|
||||
|
||||
private:
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Download::Ptr m_task;
|
||||
Net::Download::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -42,12 +42,15 @@ void XboxAuthorizationStep::perform()
|
||||
{ "Accept", "application/json" },
|
||||
};
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("XboxAuthorizationStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &XboxAuthorizationStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
qDebug() << "Getting authorization token for " << m_relyingParty;
|
||||
}
|
||||
@ -55,19 +58,19 @@ void XboxAuthorizationStep::perform()
|
||||
void XboxAuthorizationStep::onRequestDone()
|
||||
{
|
||||
qCDebug(authCredentials()) << *m_response;
|
||||
if (m_task->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_task->error();
|
||||
if (Net::isApplicationError(m_task->error())) {
|
||||
if (m_request->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_request->error();
|
||||
if (Net::isApplicationError(m_request->error())) {
|
||||
if (!processSTSError()) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||
tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, m_task->error()));
|
||||
tr("Failed to get authorization for %1 services. Error %2.").arg(m_authorizationKind, m_request->error()));
|
||||
} else {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT,
|
||||
tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, m_task->errorString()));
|
||||
tr("Unknown STS error for %1 services: %2").arg(m_authorizationKind, m_request->errorString()));
|
||||
}
|
||||
} else {
|
||||
emit finished(AccountTaskState::STATE_OFFLINE,
|
||||
tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, m_task->errorString()));
|
||||
tr("Failed to get authorization for %1 services: %2").arg(m_authorizationKind, m_request->errorString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -92,7 +95,7 @@ void XboxAuthorizationStep::onRequestDone()
|
||||
|
||||
bool XboxAuthorizationStep::processSTSError()
|
||||
{
|
||||
if (m_task->error() == QNetworkReply::AuthenticationRequiredError) {
|
||||
if (m_request->error() == QNetworkReply::AuthenticationRequiredError) {
|
||||
QJsonParseError jsonError;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(*m_response, &jsonError);
|
||||
if (jsonError.error) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/Upload.h"
|
||||
|
||||
class XboxAuthorizationStep : public AuthStep {
|
||||
@ -28,5 +29,6 @@ class XboxAuthorizationStep : public AuthStep {
|
||||
QString m_authorizationKind;
|
||||
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Upload::Ptr m_task;
|
||||
Net::Upload::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -34,25 +34,28 @@ void XboxProfileStep::perform()
|
||||
};
|
||||
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Download::makeByteArray(url, m_response);
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Download::makeByteArray(url, m_response);
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("XboxProfileStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &XboxProfileStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
qDebug() << "Getting Xbox profile...";
|
||||
}
|
||||
|
||||
void XboxProfileStep::onRequestDone()
|
||||
{
|
||||
if (m_task->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_task->error();
|
||||
if (m_request->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_request->error();
|
||||
qCDebug(authCredentials()) << *m_response;
|
||||
if (Net::isApplicationError(m_task->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to retrieve the Xbox profile: %1").arg(m_task->errorString()));
|
||||
if (Net::isApplicationError(m_request->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("Failed to retrieve the Xbox profile: %1").arg(m_request->errorString()));
|
||||
} else {
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to retrieve the Xbox profile: %1").arg(m_task->errorString()));
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("Failed to retrieve the Xbox profile: %1").arg(m_request->errorString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/Download.h"
|
||||
#include "net/NetJob.h"
|
||||
|
||||
class XboxProfileStep : public AuthStep {
|
||||
Q_OBJECT
|
||||
@ -21,5 +22,6 @@ class XboxProfileStep : public AuthStep {
|
||||
|
||||
private:
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Download::Ptr m_task;
|
||||
Net::Download::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -38,24 +38,27 @@ void XboxUserStep::perform()
|
||||
{ "x-xbl-contract-version", "1" }
|
||||
};
|
||||
m_response.reset(new QByteArray());
|
||||
m_task = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
|
||||
m_task->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
m_request = Net::Upload::makeByteArray(url, m_response, xbox_auth_data.toUtf8());
|
||||
m_request->addHeaderProxy(new Net::StaticHeaderProxy(headers));
|
||||
|
||||
m_task.reset(new NetJob("XboxUserStep", APPLICATION->network()));
|
||||
m_task->setAskRetry(false);
|
||||
m_task->addNetAction(m_request);
|
||||
|
||||
connect(m_task.get(), &Task::finished, this, &XboxUserStep::onRequestDone);
|
||||
|
||||
m_task->setNetwork(APPLICATION->network());
|
||||
m_task->start();
|
||||
qDebug() << "First layer of XBox auth ... commencing.";
|
||||
}
|
||||
|
||||
void XboxUserStep::onRequestDone()
|
||||
{
|
||||
if (m_task->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_task->error();
|
||||
if (Net::isApplicationError(m_task->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(m_task->errorString()));
|
||||
if (m_request->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "Reply error:" << m_request->error();
|
||||
if (Net::isApplicationError(m_request->error())) {
|
||||
emit finished(AccountTaskState::STATE_FAILED_SOFT, tr("XBox user authentication failed: %1").arg(m_request->errorString()));
|
||||
} else {
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(m_task->errorString()));
|
||||
emit finished(AccountTaskState::STATE_OFFLINE, tr("XBox user authentication failed: %1").arg(m_request->errorString()));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "minecraft/auth/AuthStep.h"
|
||||
#include "net/NetJob.h"
|
||||
#include "net/Upload.h"
|
||||
|
||||
class XboxUserStep : public AuthStep {
|
||||
@ -21,5 +22,6 @@ class XboxUserStep : public AuthStep {
|
||||
|
||||
private:
|
||||
std::shared_ptr<QByteArray> m_response;
|
||||
Net::Upload::Ptr m_task;
|
||||
Net::Upload::Ptr m_request;
|
||||
NetJob::Ptr m_task;
|
||||
};
|
||||
|
@ -159,15 +159,14 @@ bool Resource::enable(EnableAction action)
|
||||
if (!path.endsWith(".disabled"))
|
||||
return false;
|
||||
path.chop(9);
|
||||
|
||||
if (!file.rename(path))
|
||||
return false;
|
||||
} else {
|
||||
path += ".disabled";
|
||||
|
||||
if (!file.rename(path))
|
||||
return false;
|
||||
if (QFile::exists(path)) {
|
||||
path = FS::getUniqueResourceName(path);
|
||||
}
|
||||
}
|
||||
if (!file.rename(path))
|
||||
return false;
|
||||
|
||||
setFile(QFileInfo(path));
|
||||
|
||||
|
@ -215,9 +215,6 @@ bool ResourceFolderModel::setResourceEnabled(const QModelIndexList& indexes, Ena
|
||||
}
|
||||
|
||||
auto new_id = resource->internal_id();
|
||||
if (m_resources_index.contains(new_id)) {
|
||||
// FIXME: https://github.com/PolyMC/PolyMC/issues/550
|
||||
}
|
||||
|
||||
m_resources_index.remove(old_id);
|
||||
m_resources_index[new_id] = row;
|
||||
|
@ -7,6 +7,7 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/mod/Resource.h"
|
||||
|
||||
#include "tasks/Task.h"
|
||||
@ -50,6 +51,12 @@ class BasicFolderLoadTask : public Task {
|
||||
|
||||
m_dir.refresh();
|
||||
for (auto entry : m_dir.entryInfoList()) {
|
||||
auto filePath = entry.absoluteFilePath();
|
||||
auto newFilePath = FS::getUniqueResourceName(filePath);
|
||||
if (newFilePath != filePath) {
|
||||
FS::move(filePath, newFilePath);
|
||||
entry = QFileInfo(newFilePath);
|
||||
}
|
||||
auto resource = m_create_func(entry);
|
||||
resource->moveToThread(m_thread_to_spawn_into);
|
||||
m_result->resources.insert(resource->internal_id(), resource);
|
||||
|
@ -29,7 +29,6 @@
|
||||
#include "modplatform/ResourceAPI.h"
|
||||
#include "modplatform/flame/FlameAPI.h"
|
||||
#include "modplatform/modrinth/ModrinthAPI.h"
|
||||
#include "tasks/ConcurrentTask.h"
|
||||
#include "tasks/SequentialTask.h"
|
||||
#include "ui/pages/modplatform/ModModel.h"
|
||||
#include "ui/pages/modplatform/flame/FlameResourceModels.h"
|
||||
|
@ -36,6 +36,7 @@
|
||||
|
||||
#include "ModFolderLoadTask.h"
|
||||
|
||||
#include "FileSystem.h"
|
||||
#include "minecraft/mod/MetadataHandler.h"
|
||||
|
||||
#include <QThread>
|
||||
@ -63,6 +64,12 @@ void ModFolderLoadTask::executeTask()
|
||||
// Read JAR files that don't have metadata
|
||||
m_mods_dir.refresh();
|
||||
for (auto entry : m_mods_dir.entryInfoList()) {
|
||||
auto filePath = entry.absoluteFilePath();
|
||||
auto newFilePath = FS::getUniqueResourceName(filePath);
|
||||
if (newFilePath != filePath) {
|
||||
FS::move(filePath, newFilePath);
|
||||
entry = QFileInfo(newFilePath);
|
||||
}
|
||||
Mod* mod(new Mod(entry));
|
||||
|
||||
if (mod->enabled()) {
|
||||
|
Reference in New Issue
Block a user