From cdca53013990ac85967394529476712e6695bbf9 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Nov 2013 12:05:35 -0600 Subject: Implement account list and account list dialog --- logic/lists/MojangAccountList.cpp | 146 ++++++++++++++++++++++++++++++++++++++ logic/lists/MojangAccountList.h | 109 ++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 logic/lists/MojangAccountList.cpp create mode 100644 logic/lists/MojangAccountList.h (limited to 'logic/lists') diff --git a/logic/lists/MojangAccountList.cpp b/logic/lists/MojangAccountList.cpp new file mode 100644 index 00000000..d309d63a --- /dev/null +++ b/logic/lists/MojangAccountList.cpp @@ -0,0 +1,146 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "logic/lists/MojangAccountList.h" +#include "logic/auth/MojangAccount.h" + +MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent) +{ +} + +MojangAccountPtr MojangAccountList::findAccount(const QString &username) +{ + for (int i = 0; i < count(); i++) + { + MojangAccountPtr account = at(i); + if (account->username() == username) + return account; + } + return MojangAccountPtr(); +} + + +const MojangAccountPtr MojangAccountList::at(int i) const +{ + return MojangAccountPtr(m_accounts.at(i)); +} + +void MojangAccountList::addAccount(const MojangAccountPtr account) +{ + beginResetModel(); + m_accounts.append(account); + endResetModel(); +} + +void MojangAccountList::removeAccount(const QString& username) +{ + beginResetModel(); + for (auto account : m_accounts) + { + if (account->username() == username) + { + m_accounts.removeOne(account); + return; + } + } + endResetModel(); +} + + +int MojangAccountList::count() const +{ + return m_accounts.count(); +} + + +QVariant MojangAccountList::data(const QModelIndex &index, int role) const +{ + if (!index.isValid()) + return QVariant(); + + if (index.row() > count()) + return QVariant(); + + MojangAccountPtr account = at(index.row()); + + switch (role) + { + case Qt::DisplayRole: + switch (index.column()) + { + case NameColumn: + return account->username(); + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + return account->username(); + + case PointerRole: + return qVariantFromValue(account); + + default: + return QVariant(); + } +} + +QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + switch (section) + { + case NameColumn: + return "Name"; + + default: + return QVariant(); + } + + case Qt::ToolTipRole: + switch (section) + { + case NameColumn: + return "The name of the version."; + + default: + return QVariant(); + } + + default: + return QVariant(); + } +} + +int MojangAccountList::rowCount(const QModelIndex &parent) const +{ + // Return count + return count(); +} + +int MojangAccountList::columnCount(const QModelIndex &parent) const +{ + return 1; +} + +void MojangAccountList::updateListData(QList versions) +{ + beginResetModel(); + m_accounts = versions; + endResetModel(); +} diff --git a/logic/lists/MojangAccountList.h b/logic/lists/MojangAccountList.h new file mode 100644 index 00000000..ce16d70d --- /dev/null +++ b/logic/lists/MojangAccountList.h @@ -0,0 +1,109 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include + +#include "logic/auth/MojangAccount.h" + + +/*! + * \brief List of available Mojang accounts. + * This should be loaded in the background by MultiMC on startup. + * + * This class also inherits from QAbstractListModel. Methods from that + * class determine how this list shows up in a list view. Said methods + * all have a default implementation, but they can be overridden by subclasses to + * change the behavior of the list. + */ +class MojangAccountList : public QAbstractListModel +{ + Q_OBJECT +public: + enum ModelRoles + { + PointerRole = 0x34B1CB48 + }; + + enum VListColumns + { + // TODO: Add icon column. + // First column - Name + NameColumn = 0, + }; + + explicit MojangAccountList(QObject *parent = 0); + + //! Gets the account at the given index. + virtual const MojangAccountPtr at(int i) const; + + //! Returns the number of accounts in the list. + virtual int count() const; + + //////// List Model Functions //////// + virtual QVariant data(const QModelIndex &index, int role) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; + virtual int rowCount(const QModelIndex &parent) const; + virtual int columnCount(const QModelIndex &parent) const; + + /*! + * Adds a the given Mojang account to the account list. + */ + virtual void addAccount(const MojangAccountPtr account); + + /*! + * Removes the mojang account with the given username from the account list. + */ + virtual void removeAccount(const QString& username); + + /*! + * \brief Finds an account by its username. + * \param The username of the account to find. + * \return A const pointer to the account with the given username. NULL if + * one doesn't exist. + */ + virtual MojangAccountPtr findAccount(const QString &username); + +signals: + /*! + * Signal emitted to indicate that the account list has changed. + * This will also fire if the value of an element in the list changes (will be implemented later). + */ + void listChanged(); + +protected: + QList m_accounts; + +protected +slots: + /*! + * Updates this list with the given list of accounts. + * This is done by copying each account in the given list and inserting it + * into this one. + * We need to do this so that we can set the parents of the accounts are set to this + * account list. This can't be done in the load task, because the accounts the load + * task creates are on the load task's thread and Qt won't allow their parents + * to be set to something created on another thread. + * To get around that problem, we invoke this method on the GUI thread, which + * then copies the accounts and sets their parents correctly. + * \param accounts List of accounts whose parents should be set. + */ + virtual void updateListData(QList versions); +}; + -- cgit From a9a0b65358b3799746fa9c8e1aa879e0b59ef526 Mon Sep 17 00:00:00 2001 From: Andrew Date: Mon, 18 Nov 2013 12:58:03 -0600 Subject: Implement loading accounts from list. --- MultiMC.cpp | 6 +++ MultiMC.h | 7 +++ gui/dialogs/AccountListDialog.cpp | 7 ++- gui/dialogs/AccountListDialog.h | 5 ++- logic/auth/MojangAccount.h | 20 +++++++-- logic/lists/MojangAccountList.cpp | 89 +++++++++++++++++++++++++++++++++++++++ logic/lists/MojangAccountList.h | 14 ++++++ 7 files changed, 141 insertions(+), 7 deletions(-) (limited to 'logic/lists') diff --git a/MultiMC.cpp b/MultiMC.cpp index 1c70fb54..ae1401a6 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -10,6 +10,7 @@ #include "gui/MainWindow.h" #include "gui/dialogs/VersionSelectDialog.h" #include "logic/lists/InstanceList.h" +#include "logic/lists/MojangAccountList.h" #include "logic/lists/IconList.h" #include "logic/lists/LwjglVersionList.h" #include "logic/lists/MinecraftVersionList.h" @@ -146,6 +147,11 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) connect(InstDirSetting, SIGNAL(settingChanged(const Setting &, QVariant)), m_instances.get(), SLOT(on_InstFolderChanged(const Setting &, QVariant))); + // and accounts + m_accounts.reset(new MojangAccountList(this)); + QLOG_INFO() << "Loading accounts..."; + m_accounts->loadList(); + // init the http meta cache initHttpMetaCache(); diff --git a/MultiMC.h b/MultiMC.h index cd4a5f7d..dba923b1 100644 --- a/MultiMC.h +++ b/MultiMC.h @@ -13,6 +13,7 @@ class LWJGLVersionList; class HttpMetaCache; class SettingsObject; class InstanceList; +class MojangAccountList; class IconList; class QNetworkAccessManager; class ForgeVersionList; @@ -57,6 +58,11 @@ public: return m_instances; } + std::shared_ptr accounts() + { + return m_accounts; + } + std::shared_ptr icons(); Status status() @@ -101,6 +107,7 @@ private: std::shared_ptr m_mmc_translator; std::shared_ptr m_settings; std::shared_ptr m_instances; + std::shared_ptr m_accounts; std::shared_ptr m_icons; std::shared_ptr m_qnam; std::shared_ptr m_metacache; diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index 86d34de8..5a73cb18 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -23,13 +23,16 @@ #include #include +#include + AccountListDialog::AccountListDialog(QWidget *parent) : QDialog(parent), ui(new Ui::AccountListDialog) { ui->setupUi(this); - ui->listView->setModel(&m_accounts); + m_accounts = MMC->accounts(); + ui->listView->setModel(m_accounts.get()); } AccountListDialog::~AccountListDialog() @@ -84,7 +87,7 @@ void AccountListDialog::onLoginComplete() { // Add the authenticated account to the accounts list. MojangAccountPtr account = m_authTask->getMojangAccount(); - m_accounts.addAccount(account); + m_accounts->addAccount(account); //ui->listView->update(); } diff --git a/gui/dialogs/AccountListDialog.h b/gui/dialogs/AccountListDialog.h index 442834ef..57c8b460 100644 --- a/gui/dialogs/AccountListDialog.h +++ b/gui/dialogs/AccountListDialog.h @@ -17,6 +17,8 @@ #include +#include + #include "logic/lists/MojangAccountList.h" namespace Ui { @@ -44,8 +46,7 @@ slots: void on_closedBtnBox_rejected(); protected: - // Temporarily putting this here... - MojangAccountList m_accounts; + std::shared_ptr m_accounts; AuthenticateTask* m_authTask; diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index a38cb8f7..35261d65 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -18,9 +18,16 @@ #include #include #include +#include #include +class MojangAccount; + +typedef std::shared_ptr MojangAccountPtr; +Q_DECLARE_METATYPE(MojangAccountPtr) + + /** * Class that represents a profile within someone's Mojang account. * @@ -71,6 +78,16 @@ public: */ MojangAccount(const MojangAccount& other, QObject* parent); + /** + * Loads a MojangAccount from the given JSON object. + */ + static MojangAccountPtr loadFromJson(const QJsonObject& json); + + /** + * Saves a MojangAccount to a JSON object and returns it. + */ + QJsonObject saveToJson(); + /** * This MojangAccount's username. May be an email address if the account is migrated. @@ -130,6 +147,3 @@ protected: ProfileList m_profiles; // List of available profiles. }; -typedef std::shared_ptr MojangAccountPtr; -Q_DECLARE_METATYPE(MojangAccountPtr) - diff --git a/logic/lists/MojangAccountList.cpp b/logic/lists/MojangAccountList.cpp index d309d63a..40f0ea26 100644 --- a/logic/lists/MojangAccountList.cpp +++ b/logic/lists/MojangAccountList.cpp @@ -14,8 +14,23 @@ */ #include "logic/lists/MojangAccountList.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "logger/QsLog.h" + #include "logic/auth/MojangAccount.h" +#define DEFAULT_ACCOUNT_LIST_FILE "accounts.json" + +#define ACCOUNT_LIST_FORMAT_VERSION 1 + MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent) { } @@ -144,3 +159,77 @@ void MojangAccountList::updateListData(QList versions) m_accounts = versions; endResetModel(); } + +bool MojangAccountList::loadList(const QString& filePath) +{ + QString path = filePath; + if (path.isEmpty()) path = DEFAULT_ACCOUNT_LIST_FILE; + + QFile file(path); + + // Try to open the file and fail if we can't. + // TODO: We should probably report this error to the user. + if (!file.open(QIODevice::ReadOnly)) + { + QLOG_ERROR() << "Failed to read the account list file (" << path << ")."; + return false; + } + + // Read the file and close it. + QByteArray jsonData = file.readAll(); + file.close(); + + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); + + // Fail if the JSON is invalid. + if (parseError.error != QJsonParseError::NoError) + { + QLOG_ERROR() << QString("Failed to parse account list file: %1 at offset %2") + .arg(parseError.errorString(), QString::number(parseError.offset)) + .toUtf8(); + return false; + } + + // Make sure the root is an object. + if (!jsonDoc.isObject()) + { + QLOG_ERROR() << "Invalid account list JSON: Root should be an array."; + return false; + } + + QJsonObject root = jsonDoc.object(); + + // Make sure the format version matches. + if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION) + { + QString newName = "accountlist-old.json"; + QLOG_WARN() << "Format version mismatch when loading account list. Existing one will be renamed to \"" + << newName << "\"."; + + // Attempt to rename the old version. + file.rename(newName); + return false; + } + + // Now, load the accounts array. + beginResetModel(); + QJsonArray accounts = root.value("accounts").toArray(); + for (QJsonValue accountVal : accounts) + { + QJsonObject accountObj = accountVal.toObject(); + MojangAccountPtr account = MojangAccount::loadFromJson(accountObj); + if (account.get() != nullptr) + { + m_accounts.append(account); + } + else + { + QLOG_WARN() << "Failed to load an account."; + } + } + endResetModel(); + + return true; +} + diff --git a/logic/lists/MojangAccountList.h b/logic/lists/MojangAccountList.h index ce16d70d..37c928de 100644 --- a/logic/lists/MojangAccountList.h +++ b/logic/lists/MojangAccountList.h @@ -80,6 +80,20 @@ public: */ virtual MojangAccountPtr findAccount(const QString &username); + /*! + * \brief Loads the account list from the given file path. + * If the given file is an empty string (default), will load from the default account list file. + * \return True if successful, otherwise false. + */ + virtual bool loadList(const QString& file=""); + + /*! + * \brief Saves the account list to the given file. + * If the given file is an empty string (default), will save from the default account list file. + * \return True if successful, otherwise false. + */ + virtual bool saveList(const QString& file=""); + signals: /*! * Signal emitted to indicate that the account list has changed. -- cgit From 928e0d0b151a4690a5849c2ec4a44d97338937c5 Mon Sep 17 00:00:00 2001 From: Andrew Date: Tue, 19 Nov 2013 12:53:30 -0600 Subject: Implement saving account list to file Currently it only saves when accounts are added or removed. We'll have to fix this, but we need signals for when account objects change first. --- MultiMC.cpp | 1 + logic/auth/MojangAccount.cpp | 30 +++++++++++++++ logic/lists/MojangAccountList.cpp | 81 +++++++++++++++++++++++++++++++++++++-- logic/lists/MojangAccountList.h | 24 ++++++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) (limited to 'logic/lists') diff --git a/MultiMC.cpp b/MultiMC.cpp index ae1401a6..9179f0e0 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -150,6 +150,7 @@ MultiMC::MultiMC(int &argc, char **argv) : QApplication(argc, argv) // and accounts m_accounts.reset(new MojangAccountList(this)); QLOG_INFO() << "Loading accounts..."; + m_accounts->setListFilePath("accounts.json", true); m_accounts->loadList(); // init the http meta cache diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index 936046fb..80b88082 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -19,6 +19,8 @@ #include +#include + MojangAccount::MojangAccount(const QString& username, QObject* parent) : QObject(parent) { @@ -113,6 +115,34 @@ void MojangAccount::loadProfiles(const ProfileList& profiles) m_profiles.append(profile); } +MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject& object) +{ + // The JSON object must at least have a username for it to be valid. + if (!object.value("username").isString()) + { + QLOG_ERROR() << "Can't load Mojang account info from JSON object. Username field is missing or of the wrong type."; + return nullptr; + } + + QString username = object.value("username").toString(""); + QString clientToken = object.value("clientToken").toString(""); + QString accessToken = object.value("accessToken").toString(""); + + // TODO: Load profiles? + + return MojangAccountPtr(new MojangAccount(username, clientToken, accessToken)); +} + +QJsonObject MojangAccount::saveToJson() +{ + QJsonObject json; + json.insert("username", username()); + json.insert("clientToken", clientToken()); + json.insert("accessToken", accessToken()); + // TODO: Save profiles? + return json; +} + AccountProfile::AccountProfile(const QString& id, const QString& name) { diff --git a/logic/lists/MojangAccountList.cpp b/logic/lists/MojangAccountList.cpp index 40f0ea26..68a4da18 100644 --- a/logic/lists/MojangAccountList.cpp +++ b/logic/lists/MojangAccountList.cpp @@ -27,8 +27,6 @@ #include "logic/auth/MojangAccount.h" -#define DEFAULT_ACCOUNT_LIST_FILE "accounts.json" - #define ACCOUNT_LIST_FORMAT_VERSION 1 MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent) @@ -57,6 +55,7 @@ void MojangAccountList::addAccount(const MojangAccountPtr account) beginResetModel(); m_accounts.append(account); endResetModel(); + onListChanged(); } void MojangAccountList::removeAccount(const QString& username) @@ -71,6 +70,16 @@ void MojangAccountList::removeAccount(const QString& username) } } endResetModel(); + onListChanged(); +} + + +void MojangAccountList::onListChanged() +{ + if (m_autosave) + // TODO: Alert the user if this fails. + saveList(); + emit listChanged(); } @@ -163,7 +172,12 @@ void MojangAccountList::updateListData(QList versions) bool MojangAccountList::loadList(const QString& filePath) { QString path = filePath; - if (path.isEmpty()) path = DEFAULT_ACCOUNT_LIST_FILE; + if (path.isEmpty()) path = m_listFilePath; + if (path.isEmpty()) + { + QLOG_ERROR() << "Can't load Mojang account list. No file path given and no default set."; + return false; + } QFile file(path); @@ -233,3 +247,64 @@ bool MojangAccountList::loadList(const QString& filePath) return true; } +bool MojangAccountList::saveList(const QString& filePath) +{ + QString path(filePath); + if (path.isEmpty()) path = m_listFilePath; + if (path.isEmpty()) + { + QLOG_ERROR() << "Can't save Mojang account list. No file path given and no default set."; + return false; + } + + QLOG_INFO() << "Writing account list to \"" << path << "\"..."; + + QLOG_DEBUG() << "Building JSON data structure."; + // Build the JSON document to write to the list file. + QJsonObject root; + + root.insert("formatVersion", ACCOUNT_LIST_FORMAT_VERSION); + + // Build a list of accounts. + QLOG_DEBUG() << "Building account array."; + QJsonArray accounts; + for (MojangAccountPtr account : m_accounts) + { + QJsonObject accountObj = account->saveToJson(); + accounts.append(accountObj); + } + + // Insert the account list into the root object. + root.insert("accounts", accounts); + + // Create a JSON document object to convert our JSON to bytes. + QJsonDocument doc(root); + + + // Now that we're done building the JSON object, we can write it to the file. + QLOG_DEBUG() << "Writing account list to file."; + QFile file(path); + + // Try to open the file and fail if we can't. + // TODO: We should probably report this error to the user. + if (!file.open(QIODevice::WriteOnly)) + { + QLOG_ERROR() << "Failed to read the account list file (" << path << ")."; + return false; + } + + // Write the JSON to the file. + file.write(doc.toJson()); + file.close(); + + QLOG_INFO() << "Saved account list to \"" << path << "\"."; + + return true; +} + +void MojangAccountList::setListFilePath(QString path, bool autosave) +{ + m_listFilePath = path; + autosave = autosave; +} + diff --git a/logic/lists/MojangAccountList.h b/logic/lists/MojangAccountList.h index 37c928de..491abf6d 100644 --- a/logic/lists/MojangAccountList.h +++ b/logic/lists/MojangAccountList.h @@ -79,6 +79,15 @@ public: * one doesn't exist. */ virtual MojangAccountPtr findAccount(const QString &username); + + /*! + * Sets the default path to save the list file to. + * If autosave is true, this list will automatically save to the given path whenever it changes. + * THIS FUNCTION DOES NOT LOAD THE LIST. If you set autosave, be sure to call loadList() immediately + * after calling this function to ensure an autosaved change doesn't overwrite the list you intended + * to load. + */ + virtual void setListFilePath(QString path, bool autosave=false); /*! * \brief Loads the account list from the given file path. @@ -102,8 +111,23 @@ signals: void listChanged(); protected: + /*! + * Called whenever the list changes. + * This emits the listChanged() signal and autosaves the list (if autosave is enabled). + */ + void onListChanged(); + QList m_accounts; + //! Path to the account list file. Empty string if there isn't one. + QString m_listFilePath; + + /*! + * If true, the account list will automatically save to the account list path when it changes. + * Ignored if m_listFilePath is blank. + */ + bool m_autosave; + protected slots: /*! -- cgit From abf8408911c057d8aafe90790f5d2f5de0e1d97c Mon Sep 17 00:00:00 2001 From: Andrew Date: Wed, 20 Nov 2013 18:31:15 -0600 Subject: Nuke and pave the old login system Also, account list now saves profile lists. --- gui/MainWindow.cpp | 134 ++++++++++++++++---------------------- gui/MainWindow.h | 12 ++-- logic/BaseInstance.h | 6 +- logic/InstanceLauncher.cpp | 3 + logic/LegacyInstance.cpp | 6 +- logic/LegacyInstance.h | 4 +- logic/OneSixInstance.cpp | 16 ++--- logic/OneSixInstance.h | 6 +- logic/auth/MojangAccount.cpp | 55 ++++++++++++++-- logic/auth/MojangAccount.h | 2 +- logic/lists/MojangAccountList.cpp | 14 ++-- 11 files changed, 144 insertions(+), 114 deletions(-) (limited to 'logic/lists') diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 3bf248f9..734ed3b4 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -544,11 +544,7 @@ void MainWindow::instanceActivated(QModelIndex index) NagUtils::checkJVMArgs(inst->settings().get("JvmArgs").toString(), this); - bool autoLogin = inst->settings().get("AutoLogin").toBool(); - if (autoLogin) - doAutoLogin(); - else - doLogin(); + doLogin(); } void MainWindow::on_actionLaunchInstance_triggered() @@ -560,56 +556,34 @@ void MainWindow::on_actionLaunchInstance_triggered() } } -void MainWindow::doAutoLogin() +void MainWindow::doLogin(const QString &errorMsg) { if (!m_selectedInstance) return; - Keyring *k = Keyring::instance(); - QStringList accounts = k->getStoredAccounts("minecraft"); - - if (!accounts.isEmpty()) + // Find an account to use. + std::shared_ptr accounts = MMC->accounts(); + MojangAccountPtr account; + if (accounts->count() <= 0) { - QString username = accounts[0]; - QString password = k->getPassword("minecraft", username); - - if (!password.isEmpty()) - { - QLOG_INFO() << "Automatically logging in with stored account: " << username; - m_activeInst = m_selectedInstance; - doLogin(username, password); - } - else - { - QLOG_ERROR() << "Auto login set for account, but no password was found: " - << username; - doLogin(tr("Auto login attempted, but no password is stored.")); - } + // Tell the user they need to log in at least one account in order to play. + CustomMessageBox::selectable(this, tr("No Accounts"), + tr("In order to play Minecraft, you must have at least one Mojang or Minecraft account logged in to MultiMC. Please add an account."), + QMessageBox::Warning)->show(); } else { - QLOG_ERROR() << "Auto login set but no accounts were stored."; - doLogin(tr("Auto login attempted, but no accounts are stored.")); + // TODO: Allow user to select different accounts. + // For now, we'll just use the first one in the list until I get arround to implementing that. + account = accounts->at(0); } -} - -void MainWindow::doLogin(QString username, QString password) -{ - UserInfo uInfo{username, password}; - - ProgressDialog *tDialog = new ProgressDialog(this); - LoginTask *loginTask = new LoginTask(uInfo, tDialog); - connect(loginTask, SIGNAL(succeeded()), SLOT(onLoginComplete()), Qt::QueuedConnection); - connect(loginTask, SIGNAL(failed(QString)), SLOT(doLogin(QString)), Qt::QueuedConnection); - - tDialog->exec(loginTask); -} -void MainWindow::doLogin(const QString &errorMsg) -{ - if (!m_selectedInstance) - return; + // We'll need to validate the access token to make sure the account is still logged in. + // TODO: Do that ^ + + launchInstance(m_selectedInstance, account); + /* LoginDialog *loginDlg = new LoginDialog(this, errorMsg); if (!m_selectedInstance->lastLaunch()) loginDlg->forceOnline(); @@ -632,6 +606,41 @@ void MainWindow::doLogin(const QString &errorMsg) launchInstance(m_activeInst, m_activeLogin); } } + */ +} + +void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account) +{ + Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); + + proc = instance->prepareForLaunch(account); + if (!proc) + return; + + // Prepare GUI: If it shall stay open disable the required parts + if (MMC->settings()->get("NoHide").toBool()) + { + ui->actionLaunchInstance->setEnabled(false); + } + else + { + this->hide(); + } + + console = new ConsoleWindow(proc); + + connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, + SLOT(write(QString, MessageLevel::Enum))); + connect(proc, SIGNAL(ended(BaseInstance *)), this, SLOT(instanceEnded(BaseInstance *))); + + if (instance->settings().get("ShowConsole").toBool()) + { + console->show(); + } + + // I think this will work... + proc->setLogin(account->username(), account->accessToken()); + proc->launch(); } void MainWindow::onLoginComplete() @@ -644,7 +653,7 @@ void MainWindow::onLoginComplete() BaseUpdate *updateTask = m_activeInst->doUpdate(); if (!updateTask) { - launchInstance(m_activeInst, m_activeLogin); + //launchInstance(m_activeInst, m_activeLogin); } else { @@ -701,7 +710,7 @@ void MainWindow::onLoginComplete() void MainWindow::onGameUpdateComplete() { - launchInstance(m_activeInst, m_activeLogin); + //launchInstance(m_activeInst, m_activeLogin); } void MainWindow::onGameUpdateError(QString error) @@ -710,39 +719,6 @@ void MainWindow::onGameUpdateError(QString error) QMessageBox::Warning)->show(); } -void MainWindow::launchInstance(BaseInstance *instance, LoginResponse response) -{ - Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); - - proc = instance->prepareForLaunch(response); - if (!proc) - return; - - // Prepare GUI: If it shall stay open disable the required parts - if (MMC->settings()->get("NoHide").toBool()) - { - ui->actionLaunchInstance->setEnabled(false); - } - else - { - this->hide(); - } - - console = new ConsoleWindow(proc); - - connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, - SLOT(write(QString, MessageLevel::Enum))); - connect(proc, SIGNAL(ended(BaseInstance *)), this, SLOT(instanceEnded(BaseInstance *))); - - if (instance->settings().get("ShowConsole").toBool()) - { - console->show(); - } - - proc->setLogin(response.username, response.session_id); - proc->launch(); -} - void MainWindow::taskStart() { // Nothing to do here yet. diff --git a/gui/MainWindow.h b/gui/MainWindow.h index b89aab7c..c0fcc385 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -21,6 +21,8 @@ #include "logic/net/LoginTask.h" #include "logic/BaseInstance.h" +#include "logic/auth/MojangAccount.h" + class QToolButton; class LabeledToolButton; class QLabel; @@ -104,8 +106,12 @@ slots: void on_actionEditInstNotes_triggered(); void doLogin(const QString &errorMsg = ""); - void doLogin(QString username, QString password); - void doAutoLogin(); + + /*! + * Launches the given instance with the given account. + * This function assumes that the given account has a valid, usable access token. + */ + void launchInstance(BaseInstance* instance, MojangAccountPtr account); void onLoginComplete(); @@ -137,8 +143,6 @@ slots: void startTask(Task *task); - void launchInstance(BaseInstance *inst, LoginResponse response); - protected: bool eventFilter(QObject *obj, QEvent *ev); void setCatBackground(bool enabled); diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index b083c24a..b92d50cc 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -22,7 +22,7 @@ #include "inifile.h" #include "lists/BaseVersionList.h" -#include "net/LoginTask.h" +#include "logic/auth/MojangAccount.h" class QDialog; class BaseUpdate; @@ -153,8 +153,8 @@ public: /// returns a valid update task if update is needed, NULL otherwise virtual BaseUpdate *doUpdate() = 0; - /// returns a valid minecraft process, ready for launch - virtual MinecraftProcess *prepareForLaunch(LoginResponse response) = 0; + /// returns a valid minecraft process, ready for launch with the given account. + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) = 0; /// do any necessary cleanups after the instance finishes. also runs before /// 'prepareForLaunch' diff --git a/logic/InstanceLauncher.cpp b/logic/InstanceLauncher.cpp index 9f78e55b..c4df8220 100644 --- a/logic/InstanceLauncher.cpp +++ b/logic/InstanceLauncher.cpp @@ -38,6 +38,8 @@ void InstanceLauncher::onTerminated() void InstanceLauncher::onLoginComplete() { + // TODO: Fix this. + /* LoginTask *task = (LoginTask *)QObject::sender(); auto result = task->getResult(); auto instance = MMC->instances()->getInstanceById(instId); @@ -55,6 +57,7 @@ void InstanceLauncher::onLoginComplete() SLOT(write(QString, MessageLevel::Enum))); proc->launch(); + */ } void InstanceLauncher::doLogin(const QString &errorMsg) diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 9a91b839..3a337140 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -50,7 +50,7 @@ BaseUpdate *LegacyInstance::doUpdate() return new LegacyUpdate(this, this); } -MinecraftProcess *LegacyInstance::prepareForLaunch(LoginResponse response) +MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) { MinecraftProcess *proc = new MinecraftProcess(this); @@ -103,8 +103,8 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(LoginResponse response) #endif args << "-jar" << LAUNCHER_FILE; - args << response.player_name; - args << response.session_id; + args << account->currentProfile()->name(); + args << account->accessToken(); args << windowTitle; args << windowSize; args << lwjgl; diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index 8a8d4b91..e78bfd73 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -80,7 +80,7 @@ public: virtual void setShouldUpdate(bool val); virtual BaseUpdate *doUpdate(); - virtual MinecraftProcess *prepareForLaunch(LoginResponse response); + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account); virtual void cleanupAfterRun(); virtual QDialog *createModEditDialog(QWidget *parent); @@ -93,4 +93,4 @@ public: protected slots: virtual void jarModsChanged(); -}; \ No newline at end of file +}; diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 5c93236b..5a83bafc 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -65,7 +65,7 @@ QString replaceTokensIn(QString text, QMap with) return result; } -QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) +QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) { I_D(OneSixInstance); auto version = d->version; @@ -73,11 +73,11 @@ QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) QMap token_mapping; // yggdrasil! - token_mapping["auth_username"] = response.username; - token_mapping["auth_session"] = response.session_id; - token_mapping["auth_access_token"] = response.access_token; - token_mapping["auth_player_name"] = response.player_name; - token_mapping["auth_uuid"] = response.player_id; + token_mapping["auth_username"] = account->username(); + //token_mapping["auth_session"] = response.session_id; + token_mapping["auth_access_token"] = account->accessToken(); + token_mapping["auth_player_name"] = account->currentProfile()->name(); + token_mapping["auth_uuid"] = account->currentProfile()->id(); // this is for offline?: /* @@ -102,7 +102,7 @@ QStringList OneSixInstance::processMinecraftArgs(LoginResponse response) return parts; } -MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response) +MinecraftProcess *OneSixInstance::prepareForLaunch(MojangAccountPtr account) { I_D(OneSixInstance); cleanupAfterRun(); @@ -169,7 +169,7 @@ MinecraftProcess *OneSixInstance::prepareForLaunch(LoginResponse response) args << classPath; } args << version->mainClass; - args.append(processMinecraftArgs(response)); + args.append(processMinecraftArgs(account)); // Set the width and height for 1.6 instances bool maximize = settings().get("LaunchMaximized").toBool(); diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 2d02b605..c1c742a8 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -40,7 +40,7 @@ public: virtual QString instanceConfigFolder() const; virtual BaseUpdate *doUpdate(); - virtual MinecraftProcess *prepareForLaunch(LoginResponse response); + virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account); virtual void cleanupAfterRun(); virtual QString intendedVersionId() const; @@ -72,5 +72,5 @@ public: virtual QString getStatusbarDescription(); private: - QStringList processMinecraftArgs(LoginResponse response); -}; \ No newline at end of file + QStringList processMinecraftArgs(MojangAccountPtr account); +}; diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index 80b88082..4875e5f7 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -18,6 +18,8 @@ #include "MojangAccount.h" #include +#include +#include #include @@ -89,7 +91,12 @@ const QList MojangAccount::profiles() const const AccountProfile* MojangAccount::currentProfile() const { if (m_currentProfile < 0) - return nullptr; + { + if (m_profiles.length() > 0) + return &m_profiles.at(0); + else + return nullptr; + } else return &m_profiles.at(m_currentProfile); } @@ -128,9 +135,36 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject& object) QString clientToken = object.value("clientToken").toString(""); QString accessToken = object.value("accessToken").toString(""); - // TODO: Load profiles? + QJsonArray profileArray = object.value("profiles").toArray(); + if (profileArray.size() < 1) + { + QLOG_ERROR() << "Can't load Mojang account with username \"" << username << "\". No profiles found."; + return nullptr; + } + + ProfileList profiles; + for (QJsonValue profileVal : profileArray) + { + QJsonObject profileObject = profileVal.toObject(); + QString id = profileObject.value("id").toString(""); + QString name = profileObject.value("name").toString(""); + if (id.isEmpty() || name.isEmpty()) + { + QLOG_WARN() << "Unable to load a profile because it was missing an ID or a name."; + continue; + } + profiles.append(AccountProfile(id, name)); + } + + MojangAccountPtr account(new MojangAccount(username, clientToken, accessToken)); + account->loadProfiles(profiles); - return MojangAccountPtr(new MojangAccount(username, clientToken, accessToken)); + // Get the currently selected profile. + QString currentProfile = object.value("activeProfile").toString(""); + if (!currentProfile.isEmpty()) + account->setProfile(currentProfile); + + return account; } QJsonObject MojangAccount::saveToJson() @@ -139,7 +173,20 @@ QJsonObject MojangAccount::saveToJson() json.insert("username", username()); json.insert("clientToken", clientToken()); json.insert("accessToken", accessToken()); - // TODO: Save profiles? + + QJsonArray profileArray; + for (AccountProfile profile : m_profiles) + { + QJsonObject profileObj; + profileObj.insert("id", profile.id()); + profileObj.insert("name", profile.name()); + profileArray.append(profileObj); + } + json.insert("profiles", profileArray); + + if (currentProfile() != nullptr) + json.insert("activeProfile", currentProfile()->id()); + return json; } diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index 35261d65..e5684b77 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -123,7 +123,7 @@ public: /** * Returns a pointer to the currently selected profile. - * If no profile is selected, returns nullptr. + * If no profile is selected, returns the first profile in the profile list or nullptr if there are none. */ const AccountProfile* currentProfile() const; diff --git a/logic/lists/MojangAccountList.cpp b/logic/lists/MojangAccountList.cpp index 68a4da18..32317f84 100644 --- a/logic/lists/MojangAccountList.cpp +++ b/logic/lists/MojangAccountList.cpp @@ -185,7 +185,7 @@ bool MojangAccountList::loadList(const QString& filePath) // TODO: We should probably report this error to the user. if (!file.open(QIODevice::ReadOnly)) { - QLOG_ERROR() << "Failed to read the account list file (" << path << ")."; + QLOG_ERROR() << QString("Failed to read the account list file (%1).").arg(path).toUtf8(); return false; } @@ -217,9 +217,9 @@ bool MojangAccountList::loadList(const QString& filePath) // Make sure the format version matches. if (root.value("formatVersion").toVariant().toInt() != ACCOUNT_LIST_FORMAT_VERSION) { - QString newName = "accountlist-old.json"; - QLOG_WARN() << "Format version mismatch when loading account list. Existing one will be renamed to \"" - << newName << "\"."; + QString newName = "accounts-old.json"; + QLOG_WARN() << "Format version mismatch when loading account list. Existing one will be renamed to" + << newName; // Attempt to rename the old version. file.rename(newName); @@ -257,7 +257,7 @@ bool MojangAccountList::saveList(const QString& filePath) return false; } - QLOG_INFO() << "Writing account list to \"" << path << "\"..."; + QLOG_INFO() << "Writing account list to" << path; QLOG_DEBUG() << "Building JSON data structure."; // Build the JSON document to write to the list file. @@ -289,7 +289,7 @@ bool MojangAccountList::saveList(const QString& filePath) // TODO: We should probably report this error to the user. if (!file.open(QIODevice::WriteOnly)) { - QLOG_ERROR() << "Failed to read the account list file (" << path << ")."; + QLOG_ERROR() << QString("Failed to read the account list file (%1).").arg(path).toUtf8(); return false; } @@ -297,7 +297,7 @@ bool MojangAccountList::saveList(const QString& filePath) file.write(doc.toJson()); file.close(); - QLOG_INFO() << "Saved account list to \"" << path << "\"."; + QLOG_INFO() << "Saved account list to" << path; return true; } -- cgit From 23bc195b3c8558cb997789ca8772342612716993 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 22 Nov 2013 10:54:52 -0600 Subject: Implement removing accounts. --- gui/dialogs/AccountListDialog.cpp | 9 ++++++++- logic/lists/MojangAccountList.cpp | 8 ++++++++ logic/lists/MojangAccountList.h | 5 +++++ 3 files changed, 21 insertions(+), 1 deletion(-) (limited to 'logic/lists') diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index af074514..838bef7f 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -16,6 +16,8 @@ #include "AccountListDialog.h" #include "ui_AccountListDialog.h" +#include + #include #include @@ -48,7 +50,12 @@ void AccountListDialog::on_addAccountBtn_clicked() void AccountListDialog::on_rmAccountBtn_clicked() { - // TODO + QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); + if (selection.size() > 0) + { + QModelIndex selected = selection.first(); + m_accounts->removeAccount(selected); + } } void AccountListDialog::on_editAccountBtn_clicked() diff --git a/logic/lists/MojangAccountList.cpp b/logic/lists/MojangAccountList.cpp index 32317f84..442ef3af 100644 --- a/logic/lists/MojangAccountList.cpp +++ b/logic/lists/MojangAccountList.cpp @@ -73,6 +73,14 @@ void MojangAccountList::removeAccount(const QString& username) onListChanged(); } +void MojangAccountList::removeAccount(QModelIndex index) +{ + beginResetModel(); + m_accounts.removeAt(index.row()); + endResetModel(); + onListChanged(); +} + void MojangAccountList::onListChanged() { diff --git a/logic/lists/MojangAccountList.h b/logic/lists/MojangAccountList.h index 491abf6d..bccc2f9a 100644 --- a/logic/lists/MojangAccountList.h +++ b/logic/lists/MojangAccountList.h @@ -72,6 +72,11 @@ public: */ virtual void removeAccount(const QString& username); + /*! + * Removes the account at the given QModelIndex. + */ + virtual void removeAccount(QModelIndex index); + /*! * \brief Finds an account by its username. * \param The username of the account to find. -- cgit From 75e7932607bdd84d2867765eb6f07dcec95ee193 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 22 Nov 2013 12:47:39 -0600 Subject: Properly implement launching and downloading Also added a system to select an active account to log in with. --- gui/MainWindow.cpp | 147 +++++++++++++++----------------------- gui/MainWindow.h | 12 ++-- gui/dialogs/AccountListDialog.cpp | 12 ++++ gui/dialogs/AccountListDialog.h | 2 + gui/dialogs/AccountListDialog.ui | 12 +++- logic/lists/MojangAccountList.cpp | 37 +++++++++- logic/lists/MojangAccountList.h | 29 +++++++- 7 files changed, 148 insertions(+), 103 deletions(-) (limited to 'logic/lists') diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 9824d52f..7ea67764 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -85,11 +85,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi ui->setupUi(this); setWindowTitle(QString("MultiMC %1").arg(MMC->version().toString())); - // Set the selected instance to null - m_selectedInstance = nullptr; - // Set active instance to null. - m_activeInst = nullptr; - // OSX magic. setUnifiedTitleAndToolBarOnMac(true); @@ -563,7 +558,7 @@ void MainWindow::doLogin(const QString &errorMsg) // Find an account to use. std::shared_ptr accounts = MMC->accounts(); - MojangAccountPtr account; + MojangAccountPtr account = accounts->activeAccount(); if (accounts->count() <= 0) { // Tell the user they need to log in at least one account in order to play. @@ -577,107 +572,53 @@ void MainWindow::doLogin(const QString &errorMsg) // Open the account manager. on_actionManageAccounts_triggered(); } - return; } - else + else if (account.get() == nullptr) { - // TODO: Allow user to select different accounts. - // For now, we'll just use the first one in the list until I get arround to implementing that. - account = accounts->at(0); - } - - // We'll need to validate the access token to make sure the account is still logged in. - // TODO: Do that ^ - - launchInstance(m_selectedInstance, account); - - /* - LoginDialog *loginDlg = new LoginDialog(this, errorMsg); - if (!m_selectedInstance->lastLaunch()) - loginDlg->forceOnline(); + // Tell the user they need to log in at least one account in order to play. + auto reply = CustomMessageBox::selectable(this, tr("No Account Selected"), + tr("You don't have an account selected as an active account." + "Would you like to open the account manager to select one now?"), + QMessageBox::Information, QMessageBox::Yes | QMessageBox::No)->exec(); - loginDlg->exec(); - if (loginDlg->result() == QDialog::Accepted) - { - if (loginDlg->isOnline()) - { - m_activeInst = m_selectedInstance; - doLogin(loginDlg->getUsername(), loginDlg->getPassword()); - } - else + if (reply == QMessageBox::Yes) { - QString user = loginDlg->getUsername(); - if (user.length() == 0) - user = QString("Player"); - m_activeLogin = {user, QString("Offline"), user, QString()}; - m_activeInst = m_selectedInstance; - launchInstance(m_activeInst, m_activeLogin); + // Open the account manager. + on_actionManageAccounts_triggered(); } } - */ -} - -void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account) -{ - Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); - Q_ASSERT_X(account.get() != nullptr, "launchInstance", "account is NULL"); - - proc = instance->prepareForLaunch(account); - if (!proc) - return; - - // Prepare GUI: If it shall stay open disable the required parts - if (MMC->settings()->get("NoHide").toBool()) - { - ui->actionLaunchInstance->setEnabled(false); - } else { - this->hide(); + // We'll need to validate the access token to make sure the account is still logged in. + // TODO: Do that ^ + + prepareLaunch(m_selectedInstance, account); } - - console = new ConsoleWindow(proc); - - connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, - SLOT(write(QString, MessageLevel::Enum))); - connect(proc, SIGNAL(ended(BaseInstance *)), this, SLOT(instanceEnded(BaseInstance *))); - - if (instance->settings().get("ShowConsole").toBool()) - { - console->show(); - } - - // I think this will work... - proc->setLogin(account->username(), account->accessToken()); - proc->launch(); } -void MainWindow::onLoginComplete() +void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) { - if (!m_activeInst) - return; - LoginTask *task = (LoginTask *)QObject::sender(); - m_activeLogin = task->getResult(); - - BaseUpdate *updateTask = m_activeInst->doUpdate(); + BaseUpdate *updateTask = instance->doUpdate(); if (!updateTask) { - //launchInstance(m_activeInst, m_activeLogin); + launchInstance(instance, account); } else { ProgressDialog tDialog(this); - connect(updateTask, SIGNAL(succeeded()), SLOT(onGameUpdateComplete())); + connect(updateTask, &BaseUpdate::succeeded, [this, instance, account] { launchInstance(instance, account); }); connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); tDialog.exec(updateTask); delete updateTask; } - auto job = new NetJob("Player skin: " + m_activeLogin.player_name); + QString playerName = account->currentProfile()->name(); + + auto job = new NetJob("Player skin: " + playerName); - auto meta = MMC->metacache()->resolveEntry("skins", m_activeLogin.player_name + ".png"); + auto meta = MMC->metacache()->resolveEntry("skins", playerName + ".png"); auto action = CacheDownload::make( - QUrl("http://skins.minecraft.net/MinecraftSkins/" + m_activeLogin.player_name + ".png"), + QUrl("http://skins.minecraft.net/MinecraftSkins/" + playerName + ".png"), meta); job->addNetAction(action); meta->stale = true; @@ -702,12 +643,12 @@ void MainWindow::onLoginComplete() QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); QJsonObject root = jsonDoc.object(); QJsonObject mappings = root.value("mappings").toObject(); - QJsonArray usernames = mappings.value(m_activeLogin.username).toArray(); + QJsonArray usernames = mappings.value(account->username()).toArray(); - if (!usernames.contains(m_activeLogin.player_name)) + if (!usernames.contains(playerName)) { - usernames.prepend(m_activeLogin.player_name); - mappings[m_activeLogin.username] = usernames; + usernames.prepend(playerName); + mappings[account->username()] = usernames; root["mappings"] = mappings; jsonDoc.setObject(root); @@ -717,9 +658,39 @@ void MainWindow::onLoginComplete() } } -void MainWindow::onGameUpdateComplete() +void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account) { - //launchInstance(m_activeInst, m_activeLogin); + Q_ASSERT_X(instance != NULL, "launchInstance", "instance is NULL"); + Q_ASSERT_X(account.get() != nullptr, "launchInstance", "account is NULL"); + + proc = instance->prepareForLaunch(account); + if (!proc) + return; + + // Prepare GUI: If it shall stay open disable the required parts + if (MMC->settings()->get("NoHide").toBool()) + { + ui->actionLaunchInstance->setEnabled(false); + } + else + { + this->hide(); + } + + console = new ConsoleWindow(proc); + + connect(proc, SIGNAL(log(QString, MessageLevel::Enum)), console, + SLOT(write(QString, MessageLevel::Enum))); + connect(proc, SIGNAL(ended(BaseInstance *)), this, SLOT(instanceEnded(BaseInstance *))); + + if (instance->settings().get("ShowConsole").toBool()) + { + console->show(); + } + + // I think this will work... + proc->setLogin(account->username(), account->accessToken()); + proc->launch(); } void MainWindow::onGameUpdateError(QString error) diff --git a/gui/MainWindow.h b/gui/MainWindow.h index c0fcc385..f88905bf 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -113,9 +113,11 @@ slots: */ void launchInstance(BaseInstance* instance, MojangAccountPtr account); - void onLoginComplete(); + /*! + * Prepares the given instance for launch with the given account. + */ + void prepareLaunch(BaseInstance* instance, MojangAccountPtr account); - void onGameUpdateComplete(); void onGameUpdateError(QString error); void taskStart(); @@ -160,12 +162,6 @@ private: BaseInstance *m_selectedInstance; - // A pointer to the instance we are actively doing stuff with. - // This is set when the user launches an instance and is used to refer to that - // instance throughout the launching process. - BaseInstance *m_activeInst; - LoginResponse m_activeLogin; - Task *m_versionLoadTask; QLabel *m_statusLeft; diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index 838bef7f..c685c164 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -34,6 +34,7 @@ AccountListDialog::AccountListDialog(QWidget *parent) : ui->setupUi(this); m_accounts = MMC->accounts(); + // TODO: Make the "Active?" column show checkboxes or radio buttons. ui->listView->setModel(m_accounts.get()); } @@ -63,6 +64,17 @@ void AccountListDialog::on_editAccountBtn_clicked() // TODO } +void AccountListDialog::on_setActiveBtn_clicked() +{ + QModelIndexList selection = ui->listView->selectionModel()->selectedIndexes(); + if (selection.size() > 0) + { + QModelIndex selected = selection.first(); + MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value(); + m_accounts->setActiveAccount(account->username()); + } +} + void AccountListDialog::on_closeBtnBox_rejected() { close(); diff --git a/gui/dialogs/AccountListDialog.h b/gui/dialogs/AccountListDialog.h index 99dee639..17a50bec 100644 --- a/gui/dialogs/AccountListDialog.h +++ b/gui/dialogs/AccountListDialog.h @@ -42,6 +42,8 @@ slots: void on_editAccountBtn_clicked(); + void on_setActiveBtn_clicked(); + // This will be sent when the "close" button is clicked. void on_closeBtnBox_rejected(); diff --git a/gui/dialogs/AccountListDialog.ui b/gui/dialogs/AccountListDialog.ui index 034985a9..2872b368 100644 --- a/gui/dialogs/AccountListDialog.ui +++ b/gui/dialogs/AccountListDialog.ui @@ -27,7 +27,7 @@ - + @@ -65,6 +65,16 @@ + + + + <html><head/><body><p>Set the currently selected account as the active account. The active account is the account that is used to log in (unless it is overridden in an instance-specific setting).</p></body></html> + + + &Set Active + + + diff --git a/logic/lists/MojangAccountList.cpp b/logic/lists/MojangAccountList.cpp index 442ef3af..1d67c70f 100644 --- a/logic/lists/MojangAccountList.cpp +++ b/logic/lists/MojangAccountList.cpp @@ -33,7 +33,7 @@ MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(paren { } -MojangAccountPtr MojangAccountList::findAccount(const QString &username) +MojangAccountPtr MojangAccountList::findAccount(const QString &username) const { for (int i = 0; i < count(); i++) { @@ -41,7 +41,7 @@ MojangAccountPtr MojangAccountList::findAccount(const QString &username) if (account->username() == username) return account; } - return MojangAccountPtr(); + return nullptr; } @@ -82,6 +82,25 @@ void MojangAccountList::removeAccount(QModelIndex index) } +MojangAccountPtr MojangAccountList::activeAccount() const +{ + if (m_activeAccount.isEmpty()) + return nullptr; + else + return findAccount(m_activeAccount); +} + +void MojangAccountList::setActiveAccount(const QString& username) +{ + beginResetModel(); + for (MojangAccountPtr account : m_accounts) + if (account->username() == username) + m_activeAccount = username; + endResetModel(); + onListChanged(); +} + + void MojangAccountList::onListChanged() { if (m_autosave) @@ -112,6 +131,9 @@ QVariant MojangAccountList::data(const QModelIndex &index, int role) const case Qt::DisplayRole: switch (index.column()) { + case ActiveColumn: + return account->username() == m_activeAccount; + case NameColumn: return account->username(); @@ -137,6 +159,9 @@ QVariant MojangAccountList::headerData(int section, Qt::Orientation orientation, case Qt::DisplayRole: switch (section) { + case ActiveColumn: + return "Active?"; + case NameColumn: return "Name"; @@ -167,7 +192,7 @@ int MojangAccountList::rowCount(const QModelIndex &parent) const int MojangAccountList::columnCount(const QModelIndex &parent) const { - return 1; + return 2; } void MojangAccountList::updateListData(QList versions) @@ -251,6 +276,9 @@ bool MojangAccountList::loadList(const QString& filePath) } } endResetModel(); + + // Load the active account. + m_activeAccount = root.value("activeAccount").toString(""); return true; } @@ -285,6 +313,9 @@ bool MojangAccountList::saveList(const QString& filePath) // Insert the account list into the root object. root.insert("accounts", accounts); + // Save the active account. + root.insert("activeAccount", m_activeAccount); + // Create a JSON document object to convert our JSON to bytes. QJsonDocument doc(root); diff --git a/logic/lists/MojangAccountList.h b/logic/lists/MojangAccountList.h index bccc2f9a..71f472f7 100644 --- a/logic/lists/MojangAccountList.h +++ b/logic/lists/MojangAccountList.h @@ -44,8 +44,12 @@ public: enum VListColumns { // TODO: Add icon column. - // First column - Name - NameColumn = 0, + + // First column - Active? + ActiveColumn = 0, + + // Second column - Name + NameColumn, }; explicit MojangAccountList(QObject *parent = 0); @@ -83,7 +87,7 @@ public: * \return A const pointer to the account with the given username. NULL if * one doesn't exist. */ - virtual MojangAccountPtr findAccount(const QString &username); + virtual MojangAccountPtr findAccount(const QString &username) const; /*! * Sets the default path to save the list file to. @@ -108,6 +112,19 @@ public: */ virtual bool saveList(const QString& file=""); + /*! + * \brief Gets a pointer to the account that the user has selected as their "active" account. + * Which account is active can be overridden on a per-instance basis, but this will return the one that + * is set as active globally. + * \return The currently active MojangAccount. If there isn't an active account, returns a null pointer. + */ + virtual MojangAccountPtr activeAccount() const; + + /*! + * Sets the given account as the current active account. + */ + virtual void setActiveAccount(const QString& username); + signals: /*! * Signal emitted to indicate that the account list has changed. @@ -124,6 +141,12 @@ protected: QList m_accounts; + /*! + * Username of the account that is currently active. + * Empty string if no account is active. + */ + QString m_activeAccount; + //! Path to the account list file. Empty string if there isn't one. QString m_listFilePath; -- cgit