diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | gui/MainWindow.cpp | 84 | ||||
-rw-r--r-- | gui/MainWindow.h | 9 | ||||
-rw-r--r-- | gui/dialogs/AccountListDialog.cpp | 12 | ||||
-rw-r--r-- | gui/dialogs/ProgressDialog.cpp | 26 | ||||
-rw-r--r-- | gui/dialogs/ProgressDialog.h | 6 | ||||
-rw-r--r-- | gui/dialogs/ProgressDialog.ui | 21 | ||||
-rw-r--r-- | logic/BaseInstance.h | 2 | ||||
-rw-r--r-- | logic/LegacyInstance.cpp | 4 | ||||
-rw-r--r-- | logic/LegacyInstance.h | 2 | ||||
-rw-r--r-- | logic/OneSixInstance.cpp | 4 | ||||
-rw-r--r-- | logic/OneSixInstance.h | 2 | ||||
-rw-r--r-- | logic/OneSixUpdate.h | 1 | ||||
-rw-r--r-- | logic/auth/MojangAccount.cpp | 22 | ||||
-rw-r--r-- | logic/auth/MojangAccount.h | 3 | ||||
-rw-r--r-- | logic/auth/YggdrasilTask.cpp | 166 | ||||
-rw-r--r-- | logic/auth/YggdrasilTask.h | 55 | ||||
-rw-r--r-- | logic/auth/flows/InvalidateTask.cpp | 0 | ||||
-rw-r--r-- | logic/auth/flows/InvalidateTask.h | 0 | ||||
-rw-r--r-- | logic/auth/flows/RefreshTask.cpp | 4 | ||||
-rw-r--r-- | logic/auth/flows/ValidateTask.h | 4 | ||||
-rw-r--r-- | logic/net/NetJob.h | 2 | ||||
-rw-r--r-- | logic/tasks/ProgressProvider.h | 1 | ||||
-rw-r--r-- | logic/tasks/Task.h | 1 |
24 files changed, 223 insertions, 210 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 79a69225..beaa9bb1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -312,8 +312,6 @@ logic/auth/flows/RefreshTask.cpp logic/auth/flows/RefreshTask.cpp logic/auth/flows/ValidateTask.h logic/auth/flows/ValidateTask.cpp -logic/auth/flows/InvalidateTask.h -logic/auth/flows/InvalidateTask.cpp # legacy instances logic/LegacyInstance.h diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 69591f8b..fd35e94e 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -777,83 +777,58 @@ void MainWindow::doLaunch() accounts->setActiveAccount(account->username()); } - if (account.get() != nullptr) - { - doLaunchInst(m_selectedInstance, account); - } -} - -void MainWindow::doLaunchInst(BaseInstance* instance, MojangAccountPtr account) -{ - // We'll need to validate the access token to make sure the account is still logged in. - /* - ProgressDialog progDialog(this); - RefreshTask refreshtask(account, &progDialog); - progDialog.exec(&refreshtask); + // if no account is selected, we bail + if (!account.get()) + return; - if (refreshtask.successful()) - { - prepareLaunch(m_selectedInstance, account); - } - else + // do the login. if the account has an access token, try to refresh it first. + if(account->accountStatus() != NotVerified) { - YggdrasilTask::Error *error = refreshtask.getError(); + // We'll need to validate the access token to make sure the account is still logged in. + ProgressDialog progDialog(this); + progDialog.setSkipButton(true, tr("Play Offline")); + auto task = account->login(); + progDialog.exec(task.get()); - if (error != nullptr) + auto status = account->accountStatus(); + if(status == Online) // Online mode! Refresh the token. { - if (error->getErrorMessage().contains("invalid token", Qt::CaseInsensitive)) - { - // TODO: Allow the user to enter their password and "refresh" their access token. - if (doRefreshToken(account, tr("Your account's access token is invalid. Please enter your password to log in again."))) - doLaunchInst(instance, account); - } - else - { - CustomMessageBox::selectable( - this, tr("Access Token Validation Error"), - tr("There was an error when trying to validate your access token.\n" - "Details: %s").arg(error->getDisplayMessage()), - QMessageBox::Warning, QMessageBox::Ok)->exec(); - } + updateInstance(m_selectedInstance, account); + return; } - else + else if(status == Verified) // Offline mode with a verified account { - CustomMessageBox::selectable( - this, tr("Access Token Validation Error"), - tr("There was an unknown error when trying to validate your access token." - "The authentication server might be down, or you might not be connected to " - "the Internet."), - QMessageBox::Warning, QMessageBox::Ok)->exec(); + launchInstance(m_selectedInstance, account); + return; } } - */ + if (loginWithPassword(account, tr("Your account is currently not logged in. Please enter your password to log in again."))) + updateInstance(m_selectedInstance, account); } -bool MainWindow::doRefreshToken(MojangAccountPtr account, const QString& errorMsg) +bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString& errorMsg) { - /* EditAccountDialog passDialog(errorMsg, this, EditAccountDialog::PasswordField); if (passDialog.exec() == QDialog::Accepted) { // To refresh the token, we just create an authenticate task with the given account and the user's password. ProgressDialog progDialog(this); - AuthenticateTask authTask(account, passDialog.password(), &progDialog); - progDialog.exec(&authTask); - if (authTask.successful()) + auto task = account->login(passDialog.password()); + progDialog.exec(task.get()); + if(task->successful()) return true; else { // If the authentication task failed, recurse with the task's error message. - return doRefreshToken(account, authTask.failReason()); + return loginWithPassword(account, task->failReason()); } } - else return false;*/ return false; } -void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) +void MainWindow::updateInstance(BaseInstance* instance, MojangAccountPtr account) { - Task *updateTask = instance->doUpdate(true); + auto updateTask = instance->doUpdate(true); if (!updateTask) { launchInstance(instance, account); @@ -861,10 +836,9 @@ void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) else { ProgressDialog tDialog(this); - connect(updateTask, &Task::succeeded, [this, instance, account] { launchInstance(instance, account); }); - connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); - tDialog.exec(updateTask); - delete updateTask; + connect(updateTask.get(), &Task::succeeded, [this, instance, account] { launchInstance(instance, account); }); + connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); + tDialog.exec(updateTask.get()); } } diff --git a/gui/MainWindow.h b/gui/MainWindow.h index 59cfa3c9..62c9797e 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -110,18 +110,13 @@ slots: * If no default account is selected, prompts the user to pick an account. */ void doLaunch(); - - /*! - * Launches the given instance with the given account. - */ - void doLaunchInst(BaseInstance* instance, MojangAccountPtr account); /*! * Opens an input dialog, allowing the user to input their password and refresh its access token. * This function will execute the proper Yggdrasil task to refresh the access token. * Returns true if successful. False if the user cancelled. */ - bool doRefreshToken(MojangAccountPtr account, const QString& errorMsg=""); + bool loginWithPassword(MojangAccountPtr account, const QString& errorMsg=""); /*! * Launches the given instance with the given account. @@ -132,7 +127,7 @@ slots: /*! * Prepares the given instance for launch with the given account. */ - void prepareLaunch(BaseInstance* instance, MojangAccountPtr account); + void updateInstance(BaseInstance* instance, MojangAccountPtr account); void onGameUpdateError(QString error); diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index e1f65c16..8dae5f07 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -25,6 +25,7 @@ #include <gui/dialogs/EditAccountDialog.h> #include <gui/dialogs/ProgressDialog.h> #include <gui/dialogs/AccountSelectDialog.h> +#include <logic/tasks/Task.h> #include <MultiMC.h> @@ -117,14 +118,14 @@ void AccountListDialog::addAccount(const QString& errMsg) QString password(loginDialog.password()); MojangAccountPtr account = MojangAccount::createFromUsername(username); -/* ProgressDialog progDialog(this); - AuthenticateTask authTask(account, password, &progDialog); - if (progDialog.exec(&authTask)) + auto task = account->login(password); + progDialog.exec(task.get()); + if(task->successful()) { - // Add the authenticated account to the accounts list. - MojangAccountPtr account = authTask.getMojangAccount(); m_accounts->addAccount(account); + if (m_accounts->count() == 1) + m_accounts->setActiveAccount(account->username()); // Grab associated player skins auto job = new NetJob("Player skins: " + account->username()); @@ -141,6 +142,5 @@ void AccountListDialog::addAccount(const QString& errMsg) job->start(); } - */ } } diff --git a/gui/dialogs/ProgressDialog.cpp b/gui/dialogs/ProgressDialog.cpp index ca433dab..ba14cca2 100644 --- a/gui/dialogs/ProgressDialog.cpp +++ b/gui/dialogs/ProgressDialog.cpp @@ -25,9 +25,23 @@ ProgressDialog::ProgressDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Pr { MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); + this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + setSkipButton(false); + changeProgress(0, 100); +} + +void ProgressDialog::setSkipButton(bool present, QString label) +{ + ui->skipButton->setEnabled(present); + ui->skipButton->setVisible(present); + ui->skipButton->setText(label); updateSize(); +} - changeProgress(0, 100); +void ProgressDialog::on_skipButton_clicked(bool checked) +{ + Q_UNUSED(checked); + task->abort(); } ProgressDialog::~ProgressDialog() @@ -51,9 +65,13 @@ int ProgressDialog::exec(ProgressProvider *task) connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &))); connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64))); - // this makes sure that the task is started after the dialog is created - QMetaObject::invokeMethod(task, "start", Qt::QueuedConnection); - return QDialog::exec(); + // if this didn't connect to an already running task, invoke start + if(!task->isRunning()) + task->start(); + if(task->isRunning()) + return QDialog::exec(); + else + return 0; } ProgressProvider *ProgressDialog::getTask() diff --git a/gui/dialogs/ProgressDialog.h b/gui/dialogs/ProgressDialog.h index 0029d3ec..fe63a826 100644 --- a/gui/dialogs/ProgressDialog.h +++ b/gui/dialogs/ProgressDialog.h @@ -35,6 +35,7 @@ public: void updateSize(); int exec(ProgressProvider *task); + void setSkipButton(bool present, QString label = QString()); ProgressProvider *getTask(); @@ -47,7 +48,10 @@ slots: void changeStatus(const QString &status); void changeProgress(qint64 current, qint64 total); -signals: + +private +slots: + void on_skipButton_clicked(bool checked); protected: virtual void keyPressEvent(QKeyEvent *e); diff --git a/gui/dialogs/ProgressDialog.ui b/gui/dialogs/ProgressDialog.ui index a56d2a92..04b8fef3 100644 --- a/gui/dialogs/ProgressDialog.ui +++ b/gui/dialogs/ProgressDialog.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>68</height> + <height>100</height> </rect> </property> <property name="minimumSize"> @@ -25,8 +25,8 @@ <property name="windowTitle"> <string>Please wait...</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> <widget class="QLabel" name="statusLabel"> <property name="text"> <string>Task Status...</string> @@ -36,7 +36,7 @@ </property> </widget> </item> - <item> + <item row="1" column="0"> <widget class="QProgressBar" name="taskProgressBar"> <property name="value"> <number>24</number> @@ -46,6 +46,19 @@ </property> </widget> </item> + <item row="2" column="0"> + <widget class="QPushButton" name="skipButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Skip</string> + </property> + </widget> + </item> </layout> </widget> <resources/> diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 2d7537d6..603f6105 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -150,7 +150,7 @@ public: virtual SettingsObject &settings() const; /// returns a valid update task if update is needed, NULL otherwise - virtual Task *doUpdate(bool prepare_for_launch) = 0; + virtual std::shared_ptr<Task> doUpdate(bool prepare_for_launch) = 0; /// returns a valid minecraft process, ready for launch with the given account. virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) = 0; diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index decfd39d..55523048 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -44,12 +44,12 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, settings->registerSetting(new Setting("IntendedJarVersion", "")); } -Task *LegacyInstance::doUpdate(bool prepare_for_launch) +std::shared_ptr<Task> LegacyInstance::doUpdate(bool prepare_for_launch) { // make sure the jar mods list is initialized by asking for it. auto list = jarModList(); // create an update task - return new LegacyUpdate(this, prepare_for_launch , this); + return std::shared_ptr<Task> (new LegacyUpdate(this, prepare_for_launch , this)); } MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index a17ef281..948d4be4 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -76,7 +76,7 @@ public: virtual bool shouldUpdate() const override; virtual void setShouldUpdate(bool val) override; - virtual Task *doUpdate(bool prepare_for_launch) override; + virtual std::shared_ptr<Task> doUpdate(bool prepare_for_launch) override; virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; virtual void cleanupAfterRun() override; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 5362e894..7a1d7dad 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -37,9 +37,9 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o reloadFullVersion(); } -Task *OneSixInstance::doUpdate(bool prepare_for_launch) +std::shared_ptr<Task> OneSixInstance::doUpdate(bool prepare_for_launch) { - return new OneSixUpdate(this, prepare_for_launch); + return std::shared_ptr<Task> (new OneSixUpdate(this, prepare_for_launch)); } QString replaceTokensIn(QString text, QMap<QString, QString> with) diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 042c104b..7ea2d08b 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -39,7 +39,7 @@ public: QString loaderModsDir() const; virtual QString instanceConfigFolder() const override; - virtual Task *doUpdate(bool prepare_for_launch) override; + virtual std::shared_ptr<Task> doUpdate(bool prepare_for_launch) override; virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; virtual void cleanupAfterRun() override; diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index b86c205f..5fd2c59f 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -48,6 +48,7 @@ slots: // extract the appropriate libraries void prepareForLaunch(); + private: NetJobPtr specificVersionDownloadJob; NetJobPtr jarlibDownloadJob; diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index 9ab71077..b1acfb25 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -134,22 +134,21 @@ AccountStatus MojangAccount::accountStatus() const return Online; } -bool MojangAccount::login(QString password) +std::shared_ptr<Task> MojangAccount::login(QString password) { if(m_currentTask) - return false; + return m_currentTask; if(password.isEmpty()) { - m_currentTask.reset(new RefreshTask(this, this)); + m_currentTask.reset(new RefreshTask(this)); } else { - m_currentTask.reset(new AuthenticateTask(this, password, this)); + m_currentTask.reset(new AuthenticateTask(this, password)); } connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); - m_currentTask->start(); - return true; + return m_currentTask; } void MojangAccount::authSucceeded() @@ -161,8 +160,13 @@ void MojangAccount::authSucceeded() void MojangAccount::authFailed(QString reason) { - m_online = false; - m_accessToken = QString(); + // This is emitted when the yggdrasil tasks time out or are cancelled. + // -> we treat the error as no-op + if(reason != "Yggdrasil task cancelled.") + { + m_online = false; + m_accessToken = QString(); + emit changed(); + } m_currentTask.reset(); - emit changed(); } diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index 751aafe5..8c44ce58 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -23,6 +23,7 @@ #include <memory> +class Task; class YggdrasilTask; class MojangAccount; @@ -94,7 +95,7 @@ public: /* manipulation */ * Attempt to login. Empty password means we use the token. * If the attempt fails because we already are performing some task, it returns false. */ - bool login(QString password = QString()); + std::shared_ptr<Task> login(QString password = QString()); public: /* queries */ const QString &username() const diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index e06ae182..6b938ea7 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -30,12 +30,6 @@ YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent) { } -YggdrasilTask::~YggdrasilTask() -{ - if (m_error) - delete m_error; -} - void YggdrasilTask::executeTask() { setStatus(getStateMessage(STATE_SENDING_REQUEST)); @@ -44,107 +38,124 @@ void YggdrasilTask::executeTask() QJsonDocument doc(getRequestContent()); auto worker = MMC->qnam(); - connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this, - SLOT(processReply(QNetworkReply *))); - QUrl reqUrl("https://authserver.mojang.com/" + getEndpoint()); QNetworkRequest netRequest(reqUrl); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QByteArray requestData = doc.toJson(); m_netReply = worker->post(netRequest, requestData); + connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply); + connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers); + connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers); + timeout_keeper.setSingleShot(true); + timeout_keeper.start(timeout_max); + counter.setSingleShot(false); + counter.start(time_step); + progress(0, timeout_max); + connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abort); + connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat); } -void YggdrasilTask::processReply(QNetworkReply *reply) +void YggdrasilTask::refreshTimers(qint64, qint64) { - setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); + timeout_keeper.stop(); + timeout_keeper.start(timeout_max); + progress(count = 0, timeout_max); +} +void YggdrasilTask::heartbeat() +{ + count += time_step; + progress(count, timeout_max); +} - if (m_netReply != reply) - // Wrong reply for some reason... - return; +void YggdrasilTask::abort() +{ + progress(timeout_max, timeout_max); + m_netReply->abort(); +} + +void YggdrasilTask::processReply() +{ + setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); - if (reply->error() == QNetworkReply::OperationCanceledError) + if (m_netReply->error() == QNetworkReply::OperationCanceledError) { + // WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout emitFailed("Yggdrasil task cancelled."); return; } - else + + // Try to parse the response regardless of the response code. + // Sometimes the auth server will give more information and an error code. + QJsonParseError jsonError; + QByteArray replyData = m_netReply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); + // Check the response code. + int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (responseCode == 200) { - // Try to parse the response regardless of the response code. - // Sometimes the auth server will give more information and an error code. - QJsonParseError jsonError; - QByteArray replyData = reply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); - // Check the response code. - int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) + // If the response code was 200, then there shouldn't be an error. Make sure + // anyways. + // Also, sometimes an empty reply indicates success. If there was no data received, + // pass an empty json object to the processResponse function. + if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) { - // If the response code was 200, then there shouldn't be an error. Make sure - // anyways. - // Also, sometimes an empty reply indicates success. If there was no data received, - // pass an empty json object to the processResponse function. - if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) + if (processResponse(replyData.size() > 0 ? doc.object() : QJsonObject())) { - if (!processResponse(replyData.size() > 0 ? doc.object() : QJsonObject())) - { - YggdrasilTask::Error *err = getError(); - if (err) - emitFailed(err->getErrorMessage()); - else - emitFailed(tr("An unknown error occurred when processing the response " - "from the authentication server.")); - } - else - { - emitSucceeded(); - } - } - else - { - emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.") - .arg(jsonError.errorString()) - .arg(jsonError.offset)); + emitSucceeded(); + return; } + + // errors happened anyway? + emitFailed(m_error ? m_error->m_errorMessageVerbose + : tr("An unknown error occurred when processing the response " + "from the authentication server.")); } else { - // If the response code was not 200, then Yggdrasil may have given us information - // about the error. - // If we can parse the response, then get information from it. Otherwise just say - // there was an unknown error. - if (jsonError.error == QJsonParseError::NoError) - { - // We were able to parse the server's response. Woo! - // Call processError. If a subclass has overridden it then they'll handle their - // stuff there. - QLOG_DEBUG() << "The request failed, but the server gave us an error message. " - "Processing error."; - emitFailed(processError(doc.object())); - } - else - { - // The server didn't say anything regarding the error. Give the user an unknown - // error. - QLOG_DEBUG() << "The request failed and the server gave no error message. " - "Unknown error."; - emitFailed(tr("An unknown error occurred when trying to communicate with the " - "authentication server: %1").arg(reply->errorString())); - } + emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.") + .arg(jsonError.errorString()) + .arg(jsonError.offset)); } + return; + } + + // If the response code was not 200, then Yggdrasil may have given us information + // about the error. + // If we can parse the response, then get information from it. Otherwise just say + // there was an unknown error. + if (jsonError.error == QJsonParseError::NoError) + { + // We were able to parse the server's response. Woo! + // Call processError. If a subclass has overridden it then they'll handle their + // stuff there. + QLOG_DEBUG() << "The request failed, but the server gave us an error message. " + "Processing error."; + emitFailed(processError(doc.object())); + } + else + { + // The server didn't say anything regarding the error. Give the user an unknown + // error. + QLOG_DEBUG() << "The request failed and the server gave no error message. " + "Unknown error."; + emitFailed(tr("An unknown error occurred when trying to communicate with the " + "authentication server: %1").arg(m_netReply->errorString())); } } QString YggdrasilTask::processError(QJsonObject responseData) { QJsonValue errorVal = responseData.value("error"); - QJsonValue msgVal = responseData.value("errorMessage"); + QJsonValue errorMessageValue = responseData.value("errorMessage"); QJsonValue causeVal = responseData.value("cause"); - if (errorVal.isString() && msgVal.isString()) + if (errorVal.isString() && errorMessageValue.isString()) { - m_error = new Error(errorVal.toString(""), msgVal.toString(""), causeVal.toString("")); - return m_error->getDisplayMessage(); + m_error = std::shared_ptr<Error>(new Error{ + errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")}); + return m_error->m_errorMessageVerbose; } else { @@ -165,8 +176,3 @@ QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const return tr("Processing. Please wait."); } } - -YggdrasilTask::Error *YggdrasilTask::getError() const -{ - return this->m_error; -} diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h index 18d3dc61..1f81a2d0 100644 --- a/logic/auth/YggdrasilTask.h +++ b/logic/auth/YggdrasilTask.h @@ -19,6 +19,7 @@ #include <QString> #include <QJsonObject> +#include <QTimer> #include "logic/auth/MojangAccount.h" @@ -32,39 +33,17 @@ class YggdrasilTask : public Task Q_OBJECT public: explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0); - ~YggdrasilTask(); /** * Class describing a Yggdrasil error response. */ - class Error + struct Error { - public: - Error(const QString& shortError, const QString& errorMessage, const QString& cause) : - m_shortError(shortError), m_errorMessage(errorMessage), m_cause(cause) {} - - QString getShortError() const { return m_shortError; } - QString getErrorMessage() const { return m_errorMessage; } - QString getCause() const { return m_cause; } - - /// Gets the string to display in the GUI for describing this error. - QString getDisplayMessage() - { - return getErrorMessage(); - } - - protected: - QString m_shortError; - QString m_errorMessage; + QString m_errorMessageShort; + QString m_errorMessageVerbose; QString m_cause; }; - /** - * Returns a pointer to a YggdrasilTask::Error object if an error has occurred. - * If no error has occurred, returns a null pointer. - */ - virtual Error *getError() const; - protected: /** * Enum for describing the state of the current task. @@ -115,13 +94,25 @@ protected: */ virtual QString getStateMessage(const State state) const; - MojangAccount *m_account = nullptr; - - QNetworkReply *m_netReply; - - Error *m_error = nullptr; - protected slots: - void processReply(QNetworkReply *reply); + void processReply(); + void refreshTimers(qint64, qint64); + void heartbeat(); + +public +slots: + virtual void abort() override; + +protected: + // FIXME: segfault disaster waiting to happen + MojangAccount *m_account = nullptr; + QNetworkReply *m_netReply = nullptr; + std::shared_ptr<Error> m_error; + QTimer timeout_keeper; + QTimer counter; + int count = 0; // num msec since time reset + + const int timeout_max = 10000; + const int time_step = 50; }; diff --git a/logic/auth/flows/InvalidateTask.cpp b/logic/auth/flows/InvalidateTask.cpp deleted file mode 100644 index e69de29b..00000000 --- a/logic/auth/flows/InvalidateTask.cpp +++ /dev/null diff --git a/logic/auth/flows/InvalidateTask.h b/logic/auth/flows/InvalidateTask.h deleted file mode 100644 index e69de29b..00000000 --- a/logic/auth/flows/InvalidateTask.h +++ /dev/null diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp index 39fb493f..bd38eb10 100644 --- a/logic/auth/flows/RefreshTask.cpp +++ b/logic/auth/flows/RefreshTask.cpp @@ -143,9 +143,9 @@ QString RefreshTask::getStateMessage(const YggdrasilTask::State state) const switch (state) { case STATE_SENDING_REQUEST: - return tr("Refreshing: Sending request."); + return tr("Refreshing login token."); case STATE_PROCESSING_RESPONSE: - return tr("Refreshing: Processing response."); + return tr("Refreshing login token: Processing response."); default: return YggdrasilTask::getStateMessage(state); } diff --git a/logic/auth/flows/ValidateTask.h b/logic/auth/flows/ValidateTask.h index 9788f20b..0e34f0c3 100644 --- a/logic/auth/flows/ValidateTask.h +++ b/logic/auth/flows/ValidateTask.h @@ -13,6 +13,10 @@ * limitations under the License. */ +/* + * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME: + */ + #pragma once #include <logic/auth/YggdrasilTask.h> diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h index 021a1550..a7027e10 100644 --- a/logic/net/NetJob.h +++ b/logic/net/NetJob.h @@ -94,6 +94,8 @@ signals: public slots: virtual void start(); + // FIXME: implement + virtual void abort() {}; private slots: void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal); diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h index f6f2906a..15e453a3 100644 --- a/logic/tasks/ProgressProvider.h +++ b/logic/tasks/ProgressProvider.h @@ -38,4 +38,5 @@ public: public slots: virtual void start() = 0; + virtual void abort() = 0; }; diff --git a/logic/tasks/Task.h b/logic/tasks/Task.h index d08ef560..80d5e38b 100644 --- a/logic/tasks/Task.h +++ b/logic/tasks/Task.h @@ -44,6 +44,7 @@ public: public slots: virtual void start(); + virtual void abort() {}; protected: virtual void executeTask() = 0; |