mirror of
https://github.com/moonlight-stream/moonlight-qt
synced 2024-12-15 05:42:28 +00:00
Refuse to stream if Moonlight is known incompatible with the host GFE version
This commit is contained in:
parent
b50e5ed7e6
commit
2fbb320539
11 changed files with 164 additions and 3 deletions
|
@ -131,6 +131,7 @@ SOURCES += \
|
||||||
cli/commandlineparser.cpp \
|
cli/commandlineparser.cpp \
|
||||||
cli/quitstream.cpp \
|
cli/quitstream.cpp \
|
||||||
cli/startstream.cpp \
|
cli/startstream.cpp \
|
||||||
|
settings/compatfetcher.cpp \
|
||||||
settings/mappingfetcher.cpp \
|
settings/mappingfetcher.cpp \
|
||||||
settings/streamingpreferences.cpp \
|
settings/streamingpreferences.cpp \
|
||||||
streaming/input/abstouch.cpp \
|
streaming/input/abstouch.cpp \
|
||||||
|
@ -156,6 +157,7 @@ SOURCES += \
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
backend/nvapp.h \
|
backend/nvapp.h \
|
||||||
|
settings/compatfetcher.h \
|
||||||
settings/mappingfetcher.h \
|
settings/mappingfetcher.h \
|
||||||
utils.h \
|
utils.h \
|
||||||
backend/computerseeker.h \
|
backend/computerseeker.h \
|
||||||
|
|
|
@ -146,7 +146,8 @@ private:
|
||||||
ComputerManager::ComputerManager(QObject *parent)
|
ComputerManager::ComputerManager(QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
m_PollingRef(0),
|
m_PollingRef(0),
|
||||||
m_MdnsBrowser(nullptr)
|
m_MdnsBrowser(nullptr),
|
||||||
|
m_CompatFetcher(nullptr)
|
||||||
{
|
{
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
|
|
||||||
|
@ -159,6 +160,9 @@ ComputerManager::ComputerManager(QObject *parent)
|
||||||
}
|
}
|
||||||
settings.endArray();
|
settings.endArray();
|
||||||
|
|
||||||
|
// Fetch latest compatibility data asynchronously
|
||||||
|
m_CompatFetcher.start();
|
||||||
|
|
||||||
// To quit in a timely manner, we must block additional requests
|
// To quit in a timely manner, we must block additional requests
|
||||||
// after we receive the aboutToQuit() signal. This is neccessary
|
// after we receive the aboutToQuit() signal. This is neccessary
|
||||||
// because NvHTTP uses aboutToQuit() to abort requests in progres
|
// because NvHTTP uses aboutToQuit() to abort requests in progres
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "nvcomputer.h"
|
#include "nvcomputer.h"
|
||||||
#include "nvpairingmanager.h"
|
#include "nvpairingmanager.h"
|
||||||
|
#include "settings/compatfetcher.h"
|
||||||
|
|
||||||
#include <qmdnsengine/server.h>
|
#include <qmdnsengine/server.h>
|
||||||
#include <qmdnsengine/cache.h>
|
#include <qmdnsengine/cache.h>
|
||||||
|
@ -214,4 +215,5 @@ private:
|
||||||
QMdnsEngine::Browser* m_MdnsBrowser;
|
QMdnsEngine::Browser* m_MdnsBrowser;
|
||||||
QMdnsEngine::Cache m_MdnsCache;
|
QMdnsEngine::Cache m_MdnsCache;
|
||||||
QVector<MdnsPendingComputer*> m_PendingResolution;
|
QVector<MdnsPendingComputer*> m_PendingResolution;
|
||||||
|
CompatFetcher m_CompatFetcher;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include "nvcomputer.h"
|
#include "nvcomputer.h"
|
||||||
#include "nvapp.h"
|
#include "nvapp.h"
|
||||||
|
#include "settings/compatfetcher.h"
|
||||||
|
|
||||||
#include <QUdpSocket>
|
#include <QUdpSocket>
|
||||||
#include <QHostInfo>
|
#include <QHostInfo>
|
||||||
|
@ -50,6 +51,7 @@ NvComputer::NvComputer(QSettings& settings)
|
||||||
this->serverCodecModeSupport = 0;
|
this->serverCodecModeSupport = 0;
|
||||||
this->pendingQuit = false;
|
this->pendingQuit = false;
|
||||||
this->gpuModel = nullptr;
|
this->gpuModel = nullptr;
|
||||||
|
this->isSupportedServerVersion = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NvComputer::serialize(QSettings& settings) const
|
void NvComputer::serialize(QSettings& settings) const
|
||||||
|
@ -143,6 +145,7 @@ NvComputer::NvComputer(QString address, QString serverInfo, QSslCertificate serv
|
||||||
this->activeAddress = address;
|
this->activeAddress = address;
|
||||||
this->state = NvComputer::CS_ONLINE;
|
this->state = NvComputer::CS_ONLINE;
|
||||||
this->pendingQuit = false;
|
this->pendingQuit = false;
|
||||||
|
this->isSupportedServerVersion = CompatFetcher::isGfeVersionSupported(this->gfeVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NvComputer::wake()
|
bool NvComputer::wake()
|
||||||
|
@ -408,6 +411,7 @@ bool NvComputer::update(NvComputer& that)
|
||||||
ASSIGN_IF_CHANGED(state);
|
ASSIGN_IF_CHANGED(state);
|
||||||
ASSIGN_IF_CHANGED(gfeVersion);
|
ASSIGN_IF_CHANGED(gfeVersion);
|
||||||
ASSIGN_IF_CHANGED(appVersion);
|
ASSIGN_IF_CHANGED(appVersion);
|
||||||
|
ASSIGN_IF_CHANGED(isSupportedServerVersion);
|
||||||
ASSIGN_IF_CHANGED(maxLumaPixelsHEVC);
|
ASSIGN_IF_CHANGED(maxLumaPixelsHEVC);
|
||||||
ASSIGN_IF_CHANGED(gpuModel);
|
ASSIGN_IF_CHANGED(gpuModel);
|
||||||
ASSIGN_IF_CHANGED_AND_NONNULL(serverCert);
|
ASSIGN_IF_CHANGED_AND_NONNULL(serverCert);
|
||||||
|
|
|
@ -65,6 +65,7 @@ public:
|
||||||
int maxLumaPixelsHEVC;
|
int maxLumaPixelsHEVC;
|
||||||
int serverCodecModeSupport;
|
int serverCodecModeSupport;
|
||||||
QString gpuModel;
|
QString gpuModel;
|
||||||
|
bool isSupportedServerVersion;
|
||||||
|
|
||||||
// Persisted traits
|
// Persisted traits
|
||||||
QString localAddress;
|
QString localAddress;
|
||||||
|
|
|
@ -220,7 +220,12 @@ CenteredGridView {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (model.online) {
|
if (model.online) {
|
||||||
if (model.paired) {
|
if (!model.serverSupported) {
|
||||||
|
errorDialog.text = qsTr("The version of GeForce Experience on %1 is not supported by this build of Moonlight. You must update Moonlight to stream from %1.").arg(model.name)
|
||||||
|
errorDialog.helpText = ""
|
||||||
|
errorDialog.open()
|
||||||
|
}
|
||||||
|
else if (model.paired) {
|
||||||
// go to game view
|
// go to game view
|
||||||
var component = Qt.createComponent("AppView.qml")
|
var component = Qt.createComponent("AppView.qml")
|
||||||
var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name})
|
var appView = component.createObject(stackView, {"computerIndex": index, "objectName": model.name})
|
||||||
|
|
|
@ -42,6 +42,8 @@ QVariant ComputerModel::data(const QModelIndex& index, int role) const
|
||||||
return !computer->macAddress.isEmpty();
|
return !computer->macAddress.isEmpty();
|
||||||
case StatusUnknownRole:
|
case StatusUnknownRole:
|
||||||
return computer->state == NvComputer::CS_UNKNOWN;
|
return computer->state == NvComputer::CS_UNKNOWN;
|
||||||
|
case ServerSupportedRole:
|
||||||
|
return computer->isSupportedServerVersion;
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
@ -68,6 +70,7 @@ QHash<int, QByteArray> ComputerModel::roleNames() const
|
||||||
names[BusyRole] = "busy";
|
names[BusyRole] = "busy";
|
||||||
names[WakeableRole] = "wakeable";
|
names[WakeableRole] = "wakeable";
|
||||||
names[StatusUnknownRole] = "statusUnknown";
|
names[StatusUnknownRole] = "statusUnknown";
|
||||||
|
names[ServerSupportedRole] = "serverSupported";
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@ class ComputerModel : public QAbstractListModel
|
||||||
PairedRole,
|
PairedRole,
|
||||||
BusyRole,
|
BusyRole,
|
||||||
WakeableRole,
|
WakeableRole,
|
||||||
StatusUnknownRole
|
StatusUnknownRole,
|
||||||
|
ServerSupportedRole
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
112
app/settings/compatfetcher.cpp
Normal file
112
app/settings/compatfetcher.cpp
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
#include "compatfetcher.h"
|
||||||
|
#include "path.h"
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
#define COMPAT_VERSION "v1"
|
||||||
|
#define COMPAT_KEY "latestsupportedversion-"
|
||||||
|
|
||||||
|
CompatFetcher::CompatFetcher(QObject *parent) :
|
||||||
|
QObject(parent),
|
||||||
|
m_Nam(this)
|
||||||
|
{
|
||||||
|
// Never communicate over HTTP
|
||||||
|
m_Nam.setStrictTransportSecurityEnabled(true);
|
||||||
|
|
||||||
|
// Allow HTTP redirects
|
||||||
|
m_Nam.setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
|
||||||
|
|
||||||
|
connect(&m_Nam, &QNetworkAccessManager::finished,
|
||||||
|
this, &CompatFetcher::handleCompatInfoFetched);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompatFetcher::start()
|
||||||
|
{
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) && QT_VERSION < QT_VERSION_CHECK(5, 15, 1) && !defined(QT_NO_BEARERMANAGEMENT)
|
||||||
|
// HACK: Set network accessibility to work around QTBUG-80947 (introduced in Qt 5.14.0 and fixed in Qt 5.15.1)
|
||||||
|
QT_WARNING_PUSH
|
||||||
|
QT_WARNING_DISABLE_DEPRECATED
|
||||||
|
m_Nam.setNetworkAccessible(QNetworkAccessManager::Accessible);
|
||||||
|
QT_WARNING_POP
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QUrl url("https://moonlight-stream.org/compatibility/" COMPAT_VERSION);
|
||||||
|
QNetworkRequest request(url);
|
||||||
|
|
||||||
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
|
||||||
|
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, true);
|
||||||
|
#else
|
||||||
|
request.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// We'll get a callback when this is finished
|
||||||
|
m_Nam.get(request);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CompatFetcher::isGfeVersionSupported(QString gfeVersion)
|
||||||
|
{
|
||||||
|
QSettings settings;
|
||||||
|
|
||||||
|
if (gfeVersion.isEmpty()) {
|
||||||
|
// If we don't have a GFE version, just allow it
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString latestSupportedVersion = settings.value(COMPAT_KEY COMPAT_VERSION).toString();
|
||||||
|
if (latestSupportedVersion.isEmpty()) {
|
||||||
|
// We don't have compat data yet, so just assume it's supported
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList latestSupportedVersionQuad = latestSupportedVersion.split('.');
|
||||||
|
QStringList gfeVersionQuad = gfeVersion.split('.');
|
||||||
|
|
||||||
|
for (int i = 0;; i++) {
|
||||||
|
int actualVerVal = 0;
|
||||||
|
int latestSupportedVal = 0;
|
||||||
|
|
||||||
|
// Treat missing decimal places as 0
|
||||||
|
if (i < gfeVersionQuad.count()) {
|
||||||
|
actualVerVal = gfeVersionQuad[i].toInt();
|
||||||
|
}
|
||||||
|
if (i < latestSupportedVersionQuad.count()) {
|
||||||
|
latestSupportedVal = latestSupportedVersionQuad[i].toInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= gfeVersionQuad.count() && i >= latestSupportedVersionQuad.count()) {
|
||||||
|
// Equal versions - this is fine
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actualVerVal < latestSupportedVal) {
|
||||||
|
// Actual version is lower than latest supported - this is fine
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (actualVerVal > latestSupportedVal) {
|
||||||
|
// Actual version is greater than latest supported - this is bad
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CompatFetcher::handleCompatInfoFetched(QNetworkReply* reply)
|
||||||
|
{
|
||||||
|
Q_ASSERT(reply->isFinished());
|
||||||
|
|
||||||
|
if (reply->error() == QNetworkReply::NoError) {
|
||||||
|
// Queue the reply for deletion
|
||||||
|
reply->deleteLater();
|
||||||
|
|
||||||
|
QString version = QString(reply->readAll()).trimmed();
|
||||||
|
|
||||||
|
QSettings settings;
|
||||||
|
settings.setValue(COMPAT_KEY COMPAT_VERSION, version);
|
||||||
|
|
||||||
|
qInfo() << "Latest supported GFE server:" << version;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
qWarning() << "Failed to download latest compatibility data:" << reply->error();
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
}
|
22
app/settings/compatfetcher.h
Normal file
22
app/settings/compatfetcher.h
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
|
||||||
|
class CompatFetcher : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CompatFetcher(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
void start();
|
||||||
|
|
||||||
|
static bool isGfeVersionSupported(QString gfeVersion);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleCompatInfoFetched(QNetworkReply* reply);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager m_Nam;
|
||||||
|
};
|
|
@ -588,6 +588,11 @@ bool Session::validateLaunch(SDL_Window* testWindow)
|
||||||
{
|
{
|
||||||
QStringList warningList;
|
QStringList warningList;
|
||||||
|
|
||||||
|
if (!m_Computer->isSupportedServerVersion) {
|
||||||
|
emit displayLaunchError(tr("The version of GeForce Experience on %1 is not supported by this build of Moonlight. You must update Moonlight to stream from %1.").arg(m_Computer->name));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (m_Preferences->absoluteMouseMode && !m_App.isAppCollectorGame) {
|
if (m_Preferences->absoluteMouseMode && !m_App.isAppCollectorGame) {
|
||||||
emitLaunchWarning(tr("Your selection to enable remote desktop mouse mode may cause problems in games."));
|
emitLaunchWarning(tr("Your selection to enable remote desktop mouse mode may cause problems in games."));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue