summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--MultiMC.cpp6
-rw-r--r--MultiMC.h7
-rw-r--r--gui/MainWindow.cpp79
-rw-r--r--gui/MainWindow.h11
-rw-r--r--logic/net/URLConstants.h2
-rw-r--r--logic/status/StatusChecker.cpp137
-rw-r--r--logic/status/StatusChecker.h57
8 files changed, 303 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index dcb4cbaa..7aba8832 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -393,6 +393,10 @@ logic/news/NewsChecker.cpp
logic/news/NewsEntry.h
logic/news/NewsEntry.cpp
+# Status system
+logic/status/StatusChecker.h
+logic/status/StatusChecker.cpp
+
# legacy instances
logic/LegacyInstance.h
logic/LegacyInstance.cpp
diff --git a/MultiMC.cpp b/MultiMC.cpp
index 17fc2e0a..819091cd 100644
--- a/MultiMC.cpp
+++ b/MultiMC.cpp
@@ -20,8 +20,11 @@
#include "logic/news/NewsChecker.h"
+#include "logic/status/StatusChecker.h"
+
#include "logic/InstanceLauncher.h"
#include "logic/net/HttpMetaCache.h"
+#include "logic/net/URLConstants.h"
#include "logic/JavaUtils.h"
@@ -181,6 +184,9 @@ MultiMC::MultiMC(int &argc, char **argv, bool root_override)
// initialize the news checker
m_newsChecker.reset(new NewsChecker(NEWS_RSS_URL));
+ // initialize the status checker
+ m_statusChecker.reset(new StatusChecker());
+
// and instances
auto InstDirSetting = m_settings->getSetting("InstanceDir");
m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this));
diff --git a/MultiMC.h b/MultiMC.h
index 18c7aab7..638a442f 100644
--- a/MultiMC.h
+++ b/MultiMC.h
@@ -21,6 +21,7 @@ class JavaVersionList;
class UpdateChecker;
class NotificationChecker;
class NewsChecker;
+class StatusChecker;
#if defined(MMC)
#undef MMC
@@ -113,6 +114,11 @@ public:
return m_newsChecker;
}
+ std::shared_ptr<StatusChecker> statusChecker()
+ {
+ return m_statusChecker;
+ }
+
std::shared_ptr<LWJGLVersionList> lwjgllist();
std::shared_ptr<ForgeVersionList> forgelist();
@@ -183,6 +189,7 @@ private:
std::shared_ptr<UpdateChecker> m_updateChecker;
std::shared_ptr<NotificationChecker> m_notificationChecker;
std::shared_ptr<NewsChecker> m_newsChecker;
+ std::shared_ptr<StatusChecker> m_statusChecker;
std::shared_ptr<MojangAccountList> m_accounts;
std::shared_ptr<IconList> m_icons;
std::shared_ptr<QNetworkAccessManager> m_qnam;
diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp
index dba15e98..d5cc1d44 100644
--- a/gui/MainWindow.cpp
+++ b/gui/MainWindow.cpp
@@ -77,6 +77,8 @@
#include "logic/news/NewsChecker.h"
+#include "logic/status/StatusChecker.h"
+
#include "logic/net/URLConstants.h"
#include "logic/BaseInstance.h"
@@ -199,7 +201,27 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad()));
m_statusLeft = new QLabel(tr("No instance selected"), this);
+ m_statusRight = new QLabel(tr("No status available"), this);
+ m_statusRefresh = new QToolButton(this);
+ m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ m_statusRefresh->setIcon(
+ QPixmap(":/icons/toolbar/refresh").scaled(16, 16, Qt::KeepAspectRatio));
+
statusBar()->addPermanentWidget(m_statusLeft, 1);
+ statusBar()->addPermanentWidget(m_statusRight, 0);
+ statusBar()->addPermanentWidget(m_statusRefresh, 0);
+
+ // Start status checker
+ {
+ connect(MMC->statusChecker().get(), &StatusChecker::statusLoaded, this, &MainWindow::updateStatusUI);
+ connect(MMC->statusChecker().get(), &StatusChecker::statusLoadingFailed, this, &MainWindow::updateStatusFailedUI);
+
+ connect(m_statusRefresh, &QAbstractButton::clicked, this, &MainWindow::reloadStatus);
+ connect(&statusTimer, &QTimer::timeout, this, &MainWindow::reloadStatus);
+ statusTimer.setSingleShot(true);
+
+ reloadStatus();
+ }
// Add "manage accounts" button, right align
QWidget *spacer = new QWidget();
@@ -498,6 +520,63 @@ void MainWindow::updateNewsLabel()
}
}
+static QString convertStatus(const QString &status)
+{
+ if(status == "green") return "↑";
+ else if(status == "yellow") return "-";
+ else if(status == "red") return "↓";
+ else return "?";
+}
+
+void MainWindow::reloadStatus()
+{
+ MMC->statusChecker()->reloadStatus();
+ updateStatusUI();
+}
+
+static QString makeStatusString(const QMap<QString, QString> statuses)
+{
+ QString status = "";
+ status += "Web: " + convertStatus(statuses["minecraft.net"]);
+ status += " Account: " + convertStatus(statuses["account.mojang.com"]);
+ status += " Skins: " + convertStatus(statuses["skins.minecraft.net"]);
+ status += " Auth: " + convertStatus(statuses["authserver.mojang.com"]);
+ status += " Session: " + convertStatus(statuses["sessionserver.mojang.com"]);
+
+ return status;
+}
+
+void MainWindow::updateStatusUI()
+{
+ auto statusChecker = MMC->statusChecker();
+ auto statuses = statusChecker->getStatusEntries();
+
+ QString status = makeStatusString(statuses);
+ if(statusChecker->isLoadingStatus())
+ {
+ m_statusRefresh->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ m_statusRefresh->setText(tr("Loading..."));
+ }
+ else
+ {
+ m_statusRefresh->setToolButtonStyle(Qt::ToolButtonIconOnly);
+ m_statusRefresh->setText(tr(""));
+ }
+
+ m_statusRight->setText(status);
+
+ statusTimer.start(60 * 1000);
+}
+
+void MainWindow::updateStatusFailedUI()
+{
+ m_statusRight->setText(makeStatusString(QMap<QString, QString>()));
+ m_statusRefresh->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ m_statusRefresh->setText(tr("Failed."));
+
+ statusTimer.start(60 * 1000);
+}
+
void MainWindow::updateAvailable(QString repo, QString versionName, int versionId)
{
UpdateDialog dlg;
diff --git a/gui/MainWindow.h b/gui/MainWindow.h
index 12d76da4..eb478776 100644
--- a/gui/MainWindow.h
+++ b/gui/MainWindow.h
@@ -17,6 +17,7 @@
#include <QMainWindow>
#include <QProcess>
+#include <QTimer>
#include "logic/lists/InstanceList.h"
#include "logic/BaseInstance.h"
@@ -168,6 +169,12 @@ slots:
void repopulateAccountsMenu();
void updateNewsLabel();
+
+ void updateStatusUI();
+
+ void updateStatusFailedUI();
+
+ void reloadStatus();
/*!
* Runs the DownloadUpdateTask and installs updates.
@@ -198,8 +205,12 @@ private:
Task *m_versionLoadTask;
QLabel *m_statusLeft;
+ QLabel *m_statusRight;
+ QToolButton *m_statusRefresh;
QMenu *accountMenu;
QToolButton *accountMenuButton;
QAction *manageAccountsAction;
+
+ QTimer statusTimer;
};
diff --git a/logic/net/URLConstants.h b/logic/net/URLConstants.h
index 9579198d..8cb1f3fd 100644
--- a/logic/net/URLConstants.h
+++ b/logic/net/URLConstants.h
@@ -31,4 +31,6 @@ const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/");
const QString AUTH_BASE("authserver.mojang.com/");
const QString FORGE_LEGACY_URL("http://files.minecraftforge.net/minecraftforge/json");
const QString FORGE_GRADLE_URL("http://files.minecraftforge.net/maven/net/minecraftforge/forge/json");
+const QString MOJANG_STATUS_URL("http://status.mojang.com/check");
+const QString MOJANG_STATUS_NEWS_URL("http://status.mojang.com/news");
}
diff --git a/logic/status/StatusChecker.cpp b/logic/status/StatusChecker.cpp
new file mode 100644
index 00000000..7c856d3f
--- /dev/null
+++ b/logic/status/StatusChecker.cpp
@@ -0,0 +1,137 @@
+/* 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 "StatusChecker.h"
+
+#include <logic/net/URLConstants.h>
+
+#include <QByteArray>
+#include <QDomDocument>
+
+#include <logger/QsLog.h>
+
+StatusChecker::StatusChecker()
+{
+
+}
+
+void StatusChecker::reloadStatus()
+{
+ if (isLoadingStatus())
+ {
+ QLOG_INFO() << "Ignored request to reload status. Currently reloading already.";
+ return;
+ }
+
+ QLOG_INFO() << "Reloading status.";
+
+ NetJob* job = new NetJob("Status JSON");
+ job->addNetAction(ByteArrayDownload::make(URLConstants::MOJANG_STATUS_URL));
+ QObject::connect(job, &NetJob::succeeded, this, &StatusChecker::statusDownloadFinished);
+ QObject::connect(job, &NetJob::failed, this, &StatusChecker::statusDownloadFailed);
+ m_statusNetJob.reset(job);
+ job->start();
+}
+
+void StatusChecker::statusDownloadFinished()
+{
+ QLOG_DEBUG() << "Finished loading status JSON.";
+
+ QByteArray data;
+ {
+ ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(m_statusNetJob->first());
+ data = dl->m_data;
+ m_statusNetJob.reset();
+ }
+
+ QJsonParseError jsonError;
+ QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError);
+
+ if (jsonError.error != QJsonParseError::NoError)
+ {
+ fail("Error parsing status JSON:" + jsonError.errorString());
+ return;
+ }
+
+ if (!jsonDoc.isArray())
+ {
+ fail("Error parsing status JSON: JSON root is not an array");
+ return;
+ }
+
+ QJsonArray root = jsonDoc.array();
+
+ for(auto status = root.begin(); status != root.end(); ++status)
+ {
+ QVariantMap map = (*status).toObject().toVariantMap();
+
+ for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter)
+ {
+ QString key = iter.key();
+ QVariant value = iter.value();
+
+ if(value.type() == QVariant::Type::String)
+ {
+ m_statusEntries.insert(key, value.toString());
+ QLOG_DEBUG() << "Status JSON object: " << key << m_statusEntries[key];
+ }
+ else
+ {
+ fail("Malformed status JSON: expected status type to be a string.");
+ return;
+ }
+ }
+ }
+
+ succeed();
+}
+
+void StatusChecker::statusDownloadFailed()
+{
+ fail("Failed to load status JSON.");
+}
+
+
+QMap<QString, QString> StatusChecker::getStatusEntries() const
+{
+ return m_statusEntries;
+}
+
+bool StatusChecker::isLoadingStatus() const
+{
+ return m_statusNetJob.get() != nullptr;
+}
+
+QString StatusChecker::getLastLoadErrorMsg() const
+{
+ return m_lastLoadError;
+}
+
+void StatusChecker::succeed()
+{
+ m_lastLoadError = "";
+ QLOG_DEBUG() << "Status loading succeeded.";
+ m_statusNetJob.reset();
+ emit statusLoaded();
+}
+
+void StatusChecker::fail(const QString& errorMsg)
+{
+ m_lastLoadError = errorMsg;
+ QLOG_DEBUG() << "Failed to load status:" << errorMsg;
+ m_statusNetJob.reset();
+ emit statusLoadingFailed(errorMsg);
+}
+
diff --git a/logic/status/StatusChecker.h b/logic/status/StatusChecker.h
new file mode 100644
index 00000000..1cb01836
--- /dev/null
+++ b/logic/status/StatusChecker.h
@@ -0,0 +1,57 @@
+/* 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 <QObject>
+#include <QString>
+#include <QList>
+
+#include <logic/net/NetJob.h>
+
+class StatusChecker : public QObject
+{
+ Q_OBJECT
+public:
+ StatusChecker();
+
+ QString getLastLoadErrorMsg() const;
+
+ bool isStatusLoaded() const;
+
+ bool isLoadingStatus() const;
+
+ QMap<QString, QString> getStatusEntries() const;
+
+ void Q_SLOT reloadStatus();
+
+signals:
+ void statusLoaded();
+ void statusLoadingFailed(QString errorMsg);
+
+protected slots:
+ void statusDownloadFinished();
+ void statusDownloadFailed();
+
+protected:
+ QMap<QString, QString> m_statusEntries;
+ NetJobPtr m_statusNetJob;
+ bool m_loadedStatus;
+ QString m_lastLoadError;
+
+ void Q_SLOT succeed();
+ void Q_SLOT fail(const QString& errorMsg);
+};
+