From 7d7579d7f04f88013d7e4d064b2becec28cced61 Mon Sep 17 00:00:00 2001 From: Sky Date: Wed, 27 Nov 2013 16:56:15 +0000 Subject: [PATCH 1/2] Move "manage accounts" to right, use player skin when available --- CMakeLists.txt | 2 + gui/MainWindow.cpp | 31 +++++++++++++ gui/MainWindow.h | 4 ++ gui/MainWindow.ui | 1 - gui/dialogs/AccountListDialog.cpp | 6 ++- gui/dialogs/AccountListDialog.h | 3 ++ gui/dialogs/LoginDialog.cpp | 44 ++---------------- logic/SkinUtils.cpp | 75 +++++++++++++++++++++++++++++++ logic/SkinUtils.h | 23 ++++++++++ 9 files changed, 147 insertions(+), 42 deletions(-) create mode 100644 logic/SkinUtils.cpp create mode 100644 logic/SkinUtils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bb3126b..c831205b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -346,6 +346,8 @@ logic/JavaUtils.h logic/JavaUtils.cpp logic/NagUtils.h logic/NagUtils.cpp +logic/SkinUtils.h +logic/SkinUtils.cpp ) diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 7a0b5849..dc7a1200 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -76,6 +76,7 @@ #include "logic/OneSixUpdate.h" #include "logic/JavaUtils.h" #include "logic/NagUtils.h" +#include "logic/SkinUtils.h" #include "logic/LegacyInstance.h" @@ -164,6 +165,24 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi statusBar()->addPermanentWidget(m_statusLeft, 1); statusBar()->addPermanentWidget(m_statusRight, 0); + // Add "manage accounts" button, right align + + QWidget* spacer = new QWidget(); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + ui->mainToolBar->addWidget(spacer); + + actionManageAccounts = new QToolButton(this); + actionManageAccounts->setToolTip(tr("Manage your Mojang or Minecraft accounts.")); + actionManageAccounts->setObjectName("actionManageAccounts"); + actionManageAccounts->setText(tr("Manage accounts")); + actionManageAccounts->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + actionManageAccounts->setLayoutDirection(Qt::RightToLeft); + + activeAccountChanged(); + + connect(actionManageAccounts, SIGNAL(clicked()), this, SLOT(on_actionManageAccounts_triggered())); + ui->mainToolBar->addWidget(actionManageAccounts); + // run the things that load and download other things... FIXME: this is NOT the place // FIXME: invisible actions in the background = NOPE. { @@ -196,6 +215,16 @@ MainWindow::~MainWindow() delete assets_downloader; } +void MainWindow::activeAccountChanged() +{ + MojangAccountPtr account = MMC->accounts()->activeAccount(); + + if(account != nullptr) + { + actionManageAccounts->setIcon(SkinUtils::getFaceFromCache(account->username())); + } +} + bool MainWindow::eventFilter(QObject *obj, QEvent *ev) { if (obj == view) @@ -427,6 +456,7 @@ void MainWindow::on_actionSettings_triggered() void MainWindow::on_actionManageAccounts_triggered() { AccountListDialog dialog(this); + connect(&dialog, SIGNAL(activeAccountChanged()), SLOT(activeAccountChanged())); dialog.exec(); } @@ -624,6 +654,7 @@ void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) job->addNetAction(action); meta->stale = true; + connect(job, SIGNAL(succeeded()), SLOT(activeAccountChanged())); job->start(); auto filename = MMC->metacache()->resolveEntry("skins", "skins.json")->getFullPath(); QFile listFile(filename); diff --git a/gui/MainWindow.h b/gui/MainWindow.h index 3b8d4668..4e20d7cb 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -146,6 +146,8 @@ slots: void startTask(Task *task); + void activeAccountChanged(); + protected: bool eventFilter(QObject *obj, QEvent *ev); void setCatBackground(bool enabled); @@ -167,4 +169,6 @@ private: QLabel *m_statusLeft; QLabel *m_statusRight; + + QToolButton *actionManageAccounts; }; diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index f76d4d4e..0537d0e5 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -70,7 +70,6 @@ - diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index c685c164..81b39e4c 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -56,6 +56,8 @@ void AccountListDialog::on_rmAccountBtn_clicked() { QModelIndex selected = selection.first(); m_accounts->removeAccount(selected); + + emit activeAccountChanged(); } } @@ -72,7 +74,9 @@ void AccountListDialog::on_setActiveBtn_clicked() QModelIndex selected = selection.first(); MojangAccountPtr account = selected.data(MojangAccountList::PointerRole).value(); m_accounts->setActiveAccount(account->username()); - } + + emit activeAccountChanged(); + } } void AccountListDialog::on_closeBtnBox_rejected() diff --git a/gui/dialogs/AccountListDialog.h b/gui/dialogs/AccountListDialog.h index 17a50bec..52666e3b 100644 --- a/gui/dialogs/AccountListDialog.h +++ b/gui/dialogs/AccountListDialog.h @@ -47,6 +47,9 @@ slots: // This will be sent when the "close" button is clicked. void on_closeBtnBox_rejected(); +signals: + void activeAccountChanged(); + protected: std::shared_ptr m_accounts; diff --git a/gui/dialogs/LoginDialog.cpp b/gui/dialogs/LoginDialog.cpp index 19c6437d..28bbb1ee 100644 --- a/gui/dialogs/LoginDialog.cpp +++ b/gui/dialogs/LoginDialog.cpp @@ -18,6 +18,7 @@ #include "keyring.h" #include "gui/Platform.h" #include "MultiMC.h" +#include "logic/SkinUtils.h" #include #include @@ -168,47 +169,10 @@ void LoginDialog::userTextChanged(const QString &user) ui->rememberPasswordCheckbox->setChecked(!passwd.isEmpty()); ui->passwordTextBox->setText(passwd); - QByteArray data; - { - auto filename = - MMC->metacache()->resolveEntry("skins", "skins.json")->getFullPath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - return; - data = listFile.readAll(); - } + QPixmap face = SkinUtils::getFaceFromCache(user); + gotFace = !face.isNull(); - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - QJsonObject root = jsonDoc.object(); - QJsonObject mappings = root.value("mappings").toObject(); - - if (!mappings[user].isUndefined()) - { - QJsonArray usernames = mappings.value(user).toArray(); - if (!usernames.isEmpty()) - { - QString mapped_username = usernames[0].toString(); - - if (!mapped_username.isEmpty()) - { - QFile fskin(MMC->metacache() - ->resolveEntry("skins", mapped_username + ".png") - ->getFullPath()); - if (fskin.exists()) - { - QPixmap skin(MMC->metacache() - ->resolveEntry("skins", mapped_username + ".png") - ->getFullPath()); - QPixmap face = - skin.copy(8, 8, 8, 8).scaled(48, 48, Qt::KeepAspectRatio); - - ui->lblFace->setPixmap(face); - gotFace = true; - } - } - } - } + if(gotFace) ui->lblFace->setPixmap(face); } ui->lblFace->setVisible(gotFace); diff --git a/logic/SkinUtils.cpp b/logic/SkinUtils.cpp new file mode 100644 index 00000000..f00ce98d --- /dev/null +++ b/logic/SkinUtils.cpp @@ -0,0 +1,75 @@ +/* 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 "MultiMC.h" +#include "logic/SkinUtils.h" +#include "net/HttpMetaCache.h" + +#include +#include +#include +#include + +namespace SkinUtils +{ +QPixmap getFaceFromCache(QString username, int height, int width) +{ + bool gotFace = false; + + QByteArray data; + { + auto filename = + MMC->metacache()->resolveEntry("skins", "skins.json")->getFullPath(); + QFile listFile(filename); + if (!listFile.open(QIODevice::ReadOnly)) + return QPixmap(); + data = listFile.readAll(); + } + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + QJsonObject root = jsonDoc.object(); + QJsonObject mappings = root.value("mappings").toObject(); + + if (!mappings[username].isUndefined()) + { + QJsonArray usernames = mappings.value(username).toArray(); + if (!usernames.isEmpty()) + { + QString mapped_username = usernames[0].toString(); + + if (!mapped_username.isEmpty()) + { + QFile fskin(MMC->metacache() + ->resolveEntry("skins", mapped_username + ".png") + ->getFullPath()); + if (fskin.exists()) + { + QPixmap skin(MMC->metacache() + ->resolveEntry("skins", mapped_username + ".png") + ->getFullPath()); + + QPixmap face = + skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio); + + return face; + } + } + } + } + + if(!gotFace) return QPixmap(); +} +} diff --git a/logic/SkinUtils.h b/logic/SkinUtils.h new file mode 100644 index 00000000..324f86b8 --- /dev/null +++ b/logic/SkinUtils.h @@ -0,0 +1,23 @@ +/* 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 + +namespace SkinUtils +{ +QPixmap getFaceFromCache(QString username, int height = 48, int width = 48); +} From 38732636d3c1f1563c2dceeae723fd51f7b5fda6 Mon Sep 17 00:00:00 2001 From: Sky Date: Wed, 27 Nov 2013 18:45:29 +0000 Subject: [PATCH 2/2] Nuke skins.json, use nice yggdrasil implementation instead. Grabs all Mojang account skins on addition, active on startup --- gui/MainWindow.cpp | 71 ++++++++++--------------------- gui/dialogs/AccountListDialog.cpp | 17 ++++++++ logic/SkinUtils.cpp | 50 +++++----------------- 3 files changed, 51 insertions(+), 87 deletions(-) diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index dc7a1200..3fdbed44 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -178,7 +178,24 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi actionManageAccounts->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); actionManageAccounts->setLayoutDirection(Qt::RightToLeft); - activeAccountChanged(); + MojangAccountPtr account = MMC->accounts()->activeAccount(); + if(account != nullptr) + { + auto job = new NetJob("Startup player skins: " + account->username()); + + for(AccountProfile profile : account->profiles()) + { + auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png"); + auto action = CacheDownload::make( + QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"), + meta); + job->addNetAction(action); + meta->stale = true; + } + + connect(job, SIGNAL(succeeded()), SLOT(activeAccountChanged())); + job->start(); + } connect(actionManageAccounts, SIGNAL(clicked()), this, SLOT(on_actionManageAccounts_triggered())); ui->mainToolBar->addWidget(actionManageAccounts); @@ -221,7 +238,11 @@ void MainWindow::activeAccountChanged() if(account != nullptr) { - actionManageAccounts->setIcon(SkinUtils::getFaceFromCache(account->username())); + const AccountProfile *profile = account->currentProfile(); + if(profile != nullptr) + { + actionManageAccounts->setIcon(SkinUtils::getFaceFromCache(profile->name())); + } } } @@ -642,52 +663,6 @@ void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) 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; - - connect(job, SIGNAL(succeeded()), SLOT(activeAccountChanged())); - 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) diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index 81b39e4c..ea6861c6 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -112,5 +113,21 @@ void AccountListDialog::onLoginComplete() MojangAccountPtr account = m_authTask->getMojangAccount(); m_accounts->addAccount(account); //ui->listView->update(); + + // Grab associated player skins + auto job = new NetJob("Player skins: " + account->username()); + + for(AccountProfile profile : account->profiles()) + { + auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png"); + auto action = CacheDownload::make( + QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"), + meta); + job->addNetAction(action); + meta->stale = true; + } + + connect(job, SIGNAL(succeeded()), SIGNAL(activeAccountChanged())); + job->start(); } diff --git a/logic/SkinUtils.cpp b/logic/SkinUtils.cpp index f00ce98d..c6c80006 100644 --- a/logic/SkinUtils.cpp +++ b/logic/SkinUtils.cpp @@ -24,52 +24,24 @@ namespace SkinUtils { +/* + * Given a username, return a pixmap of the cached skin (if it exists), QPixmap() otherwise + */ QPixmap getFaceFromCache(QString username, int height, int width) { - bool gotFace = false; + QFile fskin(MMC->metacache() + ->resolveEntry("skins", username + ".png") + ->getFullPath()); - QByteArray data; + if (fskin.exists()) { - auto filename = - MMC->metacache()->resolveEntry("skins", "skins.json")->getFullPath(); - QFile listFile(filename); - if (!listFile.open(QIODevice::ReadOnly)) - return QPixmap(); - data = listFile.readAll(); - } - - QJsonParseError jsonError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); - QJsonObject root = jsonDoc.object(); - QJsonObject mappings = root.value("mappings").toObject(); - - if (!mappings[username].isUndefined()) - { - QJsonArray usernames = mappings.value(username).toArray(); - if (!usernames.isEmpty()) + QPixmap skin(fskin.fileName()); + if(!skin.isNull()) { - QString mapped_username = usernames[0].toString(); - - if (!mapped_username.isEmpty()) - { - QFile fskin(MMC->metacache() - ->resolveEntry("skins", mapped_username + ".png") - ->getFullPath()); - if (fskin.exists()) - { - QPixmap skin(MMC->metacache() - ->resolveEntry("skins", mapped_username + ".png") - ->getFullPath()); - - QPixmap face = - skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio); - - return face; - } - } + return skin.copy(8, 8, 8, 8).scaled(height, width, Qt::KeepAspectRatio); } } - if(!gotFace) return QPixmap(); + return QPixmap(); } }