From 75e7932607bdd84d2867765eb6f07dcec95ee193 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 22 Nov 2013 12:47:39 -0600 Subject: [PATCH] Properly implement launching and downloading Also added a system to select an active account to log in with. --- gui/MainWindow.cpp | 185 +++++++++++++----------------- 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, 167 insertions(+), 122 deletions(-) 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,44 +572,90 @@ void MainWindow::doLogin(const QString &errorMsg) // Open the account manager. on_actionManageAccounts_triggered(); } - return; + } + else if (account.get() == nullptr) + { + // 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(); + + if (reply == QMessageBox::Yes) + { + // Open the account manager. + on_actionManageAccounts_triggered(); + } } else { - // 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 ^ + + prepareLaunch(m_selectedInstance, account); } +} - // 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(); - - loginDlg->exec(); - if (loginDlg->result() == QDialog::Accepted) +void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) +{ + BaseUpdate *updateTask = instance->doUpdate(); + if (!updateTask) { - if (loginDlg->isOnline()) - { - m_activeInst = m_selectedInstance; - doLogin(loginDlg->getUsername(), loginDlg->getPassword()); - } - else - { - 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); - } + launchInstance(instance, account); + } + else + { + ProgressDialog tDialog(this); + connect(updateTask, &BaseUpdate::succeeded, [this, instance, account] { launchInstance(instance, account); }); + connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); + tDialog.exec(updateTask); + delete updateTask; + } + + QString playerName = account->currentProfile()->name(); + + auto job = new NetJob("Player skin: " + playerName); + + auto meta = MMC->metacache()->resolveEntry("skins", playerName + ".png"); + auto action = CacheDownload::make( + QUrl("http://skins.minecraft.net/MinecraftSkins/" + playerName + ".png"), + meta); + job->addNetAction(action); + meta->stale = true; + + job->start(); + auto filename = MMC->metacache()->resolveEntry("skins", "skins.json")->getFullPath(); + QFile listFile(filename); + + // Add skin mapping + QByteArray data; + { + if (!listFile.open(QIODevice::ReadWrite)) + { + QLOG_ERROR() << "Failed to open/make skins list JSON"; + return; + } + + data = listFile.readAll(); + } + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + QJsonObject root = jsonDoc.object(); + QJsonObject mappings = root.value("mappings").toObject(); + QJsonArray usernames = mappings.value(account->username()).toArray(); + + if (!usernames.contains(playerName)) + { + usernames.prepend(playerName); + mappings[account->username()] = usernames; + root["mappings"] = mappings; + jsonDoc.setObject(root); + + // QJson hack - shouldn't have to clear the file every time a save happens + listFile.resize(0); + listFile.write(jsonDoc.toJson()); } - */ } void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account) @@ -652,76 +693,6 @@ void MainWindow::launchInstance(BaseInstance *instance, MojangAccountPtr account proc->launch(); } -void MainWindow::onLoginComplete() -{ - if (!m_activeInst) - return; - LoginTask *task = (LoginTask *)QObject::sender(); - m_activeLogin = task->getResult(); - - BaseUpdate *updateTask = m_activeInst->doUpdate(); - if (!updateTask) - { - //launchInstance(m_activeInst, m_activeLogin); - } - else - { - ProgressDialog tDialog(this); - connect(updateTask, SIGNAL(succeeded()), SLOT(onGameUpdateComplete())); - connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); - tDialog.exec(updateTask); - delete updateTask; - } - - auto job = new NetJob("Player skin: " + m_activeLogin.player_name); - - auto meta = MMC->metacache()->resolveEntry("skins", m_activeLogin.player_name + ".png"); - auto action = CacheDownload::make( - QUrl("http://skins.minecraft.net/MinecraftSkins/" + m_activeLogin.player_name + ".png"), - meta); - job->addNetAction(action); - meta->stale = true; - - job->start(); - auto filename = MMC->metacache()->resolveEntry("skins", "skins.json")->getFullPath(); - QFile listFile(filename); - - // Add skin mapping - QByteArray data; - { - if (!listFile.open(QIODevice::ReadWrite)) - { - QLOG_ERROR() << "Failed to open/make skins list JSON"; - return; - } - - data = listFile.readAll(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - QJsonObject root = jsonDoc.object(); - QJsonObject mappings = root.value("mappings").toObject(); - QJsonArray usernames = mappings.value(m_activeLogin.username).toArray(); - - if (!usernames.contains(m_activeLogin.player_name)) - { - usernames.prepend(m_activeLogin.player_name); - mappings[m_activeLogin.username] = usernames; - root["mappings"] = mappings; - jsonDoc.setObject(root); - - // QJson hack - shouldn't have to clear the file every time a save happens - listFile.resize(0); - listFile.write(jsonDoc.toJson()); - } -} - -void MainWindow::onGameUpdateComplete() -{ - //launchInstance(m_activeInst, m_activeLogin); -} - void MainWindow::onGameUpdateError(QString error) { CustomMessageBox::selectable(this, tr("Error updating instance"), 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;