From b682d69e494acdbf5fc97a81109faa667ad1918f Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Sun, 13 Jun 2021 22:16:41 -0500 Subject: Use new HTTP-based master server client --- src/networkmanager.cpp | 258 +++++++++++++++++-------------------------------- 1 file changed, 91 insertions(+), 167 deletions(-) (limited to 'src/networkmanager.cpp') diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp index 5e29e21..14c2686 100644 --- a/src/networkmanager.cpp +++ b/src/networkmanager.cpp @@ -4,209 +4,134 @@ #include "debug_functions.h" #include "lobby.h" +#include +#include +#include + NetworkManager::NetworkManager(AOApplication *parent) : QObject(parent) { ao_app = parent; - ms_socket = new QTcpSocket(this); server_socket = new QTcpSocket(this); + http = new QNetworkAccessManager(this); - ms_reconnect_timer = new QTimer(this); - ms_reconnect_timer->setSingleShot(true); - QObject::connect(ms_reconnect_timer, SIGNAL(timeout()), this, - SLOT(retry_ms_connect())); - - QObject::connect(ms_socket, SIGNAL(readyRead()), this, - SLOT(handle_ms_packet())); - QObject::connect(server_socket, SIGNAL(readyRead()), this, + connect(server_socket, SIGNAL(readyRead()), this, SLOT(handle_server_packet())); - QObject::connect(server_socket, SIGNAL(disconnected()), ao_app, + connect(server_socket, SIGNAL(disconnected()), ao_app, SLOT(server_disconnected())); QString master_config = ao_app->configini->value("master", "").value(); - if (master_config != "") - ms_nosrv_hostname = master_config; + if (!master_config.isEmpty()) + ms_baseurl = master_config; } NetworkManager::~NetworkManager() {} -void NetworkManager::connect_to_master() -{ - ms_socket->close(); - ms_socket->abort(); - -#ifdef MS_FAILOVER_SUPPORTED - perform_srv_lookup(); -#else - connect_to_master_nosrv(); -#endif -} - -void NetworkManager::connect_to_master_nosrv() -{ - QObject::connect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, - SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); - - QObject::connect(ms_socket, SIGNAL(connected()), this, - SLOT(on_ms_nosrv_connect_success())); - ms_socket->connectToHost(ms_nosrv_hostname, ms_port); -} - -void NetworkManager::connect_to_server(server_type p_server) -{ - server_socket->close(); - server_socket->abort(); - - server_socket->connectToHost(p_server.ip, p_server.port); -} - -void NetworkManager::ship_ms_packet(QString p_packet) +void NetworkManager::get_server_list(const std::function &cb) { - if (!ms_socket->isOpen()) { - retry_ms_connect(); - } - else { - ms_socket->write(p_packet.toUtf8()); - } -} + QNetworkRequest req(QUrl(ms_baseurl + "/servers")); + req.setRawHeader("User-Agent", get_user_agent().toUtf8()); + req.setTransferTimeout(timeout_milliseconds); -void NetworkManager::ship_server_packet(QString p_packet) -{ - server_socket->write(p_packet.toUtf8()); + QNetworkReply *reply = http->get(req); + connect(reply, &QNetworkReply::finished, + this, std::bind(&NetworkManager::ms_request_finished, this, reply, cb)); } -void NetworkManager::handle_ms_packet() +void NetworkManager::ms_request_finished(QNetworkReply *reply, + const std::function &cb) { - QByteArray buffer = ms_socket->readAll(); - QString in_data = QString::fromUtf8(buffer, buffer.size()); - - if (!in_data.endsWith("%")) { - ms_partial_packet = true; - ms_temp_packet += in_data; + QJsonDocument json = QJsonDocument::fromJson(reply->readAll()); + if (json.isNull()) { + qCritical().noquote() << "Invalid JSON response from" << reply->url(); + reply->deleteLater(); return; } - else { - if (ms_partial_packet) { - in_data = ms_temp_packet + in_data; - ms_temp_packet = ""; - ms_partial_packet = false; - } + qDebug().noquote() << "Got valid response from" << reply->url(); + + QVector server_list; + const auto jsonEntries = json.array(); + for (const auto &entryRef : jsonEntries) { + const auto entry = entryRef.toObject(); + server_type server; + server.ip = entry["ip"].toString(); + server.port = entry["port"].toInt(); + server.name = entry["name"].toString(); + server.desc = entry["description"].toString(tr("No description provided.")); + server_list.append(server); } + ao_app->set_server_list(server_list); - QStringList packet_list = - in_data.split("%", QString::SplitBehavior(QString::SkipEmptyParts)); + cb(); - for (QString packet : packet_list) { - AOPacket *f_packet = new AOPacket(packet); - - ao_app->ms_packet_received(f_packet); - } + reply->deleteLater(); } -void NetworkManager::perform_srv_lookup() +void NetworkManager::heartbeat_playing() { -#ifdef MS_FAILOVER_SUPPORTED - ms_dns = new QDnsLookup(QDnsLookup::SRV, ms_srv_hostname, this); - - connect(ms_dns, SIGNAL(finished()), this, SLOT(on_srv_lookup())); - ms_dns->lookup(); -#endif + // Ping the server periodically to tell the MS that you've been playing + // within a 5 minute window, so that the the number of people playing within + // that time period can be counted and an accurate player count be displayed. + // What do I care about your personal information, I really don't want it. + QNetworkRequest req(QUrl(ms_baseurl + "/playing")); + req.setRawHeader("User-Agent", get_user_agent().toUtf8()); + req.setTransferTimeout(timeout_milliseconds); + + http->post(req, QByteArray()); } -void NetworkManager::on_srv_lookup() +void NetworkManager::request_document(MSDocumentType document_type, + const std::function &cb) { -#ifdef MS_FAILOVER_SUPPORTED - bool connected = false; - if (ms_dns->error() != QDnsLookup::NoError) { - qWarning("SRV lookup of the master server DNS failed."); - ms_dns->deleteLater(); - } - else { - const auto srv_records = ms_dns->serviceRecords(); - - for (const QDnsServiceRecord &record : srv_records) { -#ifdef DEBUG_NETWORK - qDebug() << "Connecting to " << record.target() << ":" << record.port(); -#endif - ms_socket->connectToHost(record.target(), record.port()); - QElapsedTimer timer; - timer.start(); - do { - ao_app->processEvents(); - if (ms_socket->state() == QAbstractSocket::ConnectedState) { - connected = true; - break; - } - else if (ms_socket->state() != QAbstractSocket::ConnectingState && - ms_socket->state() != QAbstractSocket::HostLookupState && - ms_socket->error() != -1) { - qDebug() << ms_socket->error(); - qWarning() << "Error connecting to master server:" - << ms_socket->errorString(); - ms_socket->abort(); - ms_socket->close(); - break; - } - } while (timer.elapsed() < - timeout_milliseconds); // Very expensive spin-wait loop - it will - // bring CPU to 100%! - if (connected) { - // Connect a one-shot signal in case the master server disconnects - // randomly - QObject::connect( - ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, - SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); - break; - } - else { - ms_socket->abort(); - ms_socket->close(); - } - } - } - - // Failover to non-SRV connection - if (!connected) - connect_to_master_nosrv(); - else - emit ms_connect_finished(connected, false); -#endif + const QMap endpoints { + // I have to balance an evil with a good + { MSDocumentType::PrivacyPolicy, "/privacy" }, + { MSDocumentType::Motd, "/motd" }, + { MSDocumentType::ClientVersion, "/version" } + }; + + const QString &endpoint = endpoints[document_type]; + QNetworkRequest req(QUrl(ms_baseurl + endpoint)); + req.setRawHeader("User-Agent", get_user_agent().toUtf8()); + + QString language = + ao_app->configini->value("language").toString(); + if (language.trimmed().isEmpty()) + language = QLocale::system().name(); + + req.setRawHeader("Accept-Language", language.toUtf8()); + req.setTransferTimeout(5000); + + qDebug().noquote().nospace() + << "Getting " << endpoint << ", Accept-Language: " << language; + + QNetworkReply *reply = http->get(req); + connect(reply, &QNetworkReply::finished, this, [endpoint, cb, reply] { + QString content = QString::fromUtf8(reply->readAll()); + int http_status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if (content.isEmpty() || http_status != 200) { + qDebug().noquote().nospace() + << "Failed to get " << endpoint << " (" << reply->errorString() << ") " + << "(http status " << http_status << ")"; + content = QString(); + } cb(content); + reply->deleteLater(); + }); } -void NetworkManager::on_ms_nosrv_connect_success() -{ - emit ms_connect_finished(true, false); - - QObject::disconnect(ms_socket, SIGNAL(connected()), this, - SLOT(on_ms_nosrv_connect_success())); - - QObject::connect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, - SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); -} - -void NetworkManager::on_ms_socket_error(QAbstractSocket::SocketError error) +void NetworkManager::connect_to_server(server_type p_server) { - qWarning() << "Master server socket error:" << ms_socket->errorString() << "(" - << error << ")"; - - // Disconnect the one-shot signal - this way, failover connect attempts - // don't trigger a full retry - QObject::disconnect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), - this, - SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); - - emit ms_connect_finished(false, true); + server_socket->close(); + server_socket->abort(); - ms_reconnect_timer->start(ms_reconnect_delay * 1000); + server_socket->connectToHost(p_server.ip, p_server.port); } -void NetworkManager::retry_ms_connect() +void NetworkManager::ship_server_packet(QString p_packet) { - if (!ms_reconnect_timer->isActive() && - ms_socket->state() != QAbstractSocket::ConnectingState) - connect_to_master(); + server_socket->write(p_packet.toUtf8()); } void NetworkManager::handle_server_packet() @@ -228,10 +153,9 @@ void NetworkManager::handle_server_packet() } } - QStringList packet_list = - in_data.split("%", QString::SplitBehavior(QString::SkipEmptyParts)); + const QStringList packet_list = in_data.split("%", Qt::SkipEmptyParts); - for (QString packet : packet_list) { + for (const QString &packet : packet_list) { AOPacket *f_packet = new AOPacket(packet); ao_app->server_packet_received(f_packet); -- cgit From f1107aeac51658a0525d4231b2f1f39131acbcb5 Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Sat, 18 Dec 2021 20:29:55 -0600 Subject: Finish out feature - Add periodic timer for heartbeat - Add option to settings for opting out from heartbeat/player metrics - Change base URL to permanent URL --- src/networkmanager.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/networkmanager.cpp') diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp index 14c2686..f886c76 100644 --- a/src/networkmanager.cpp +++ b/src/networkmanager.cpp @@ -14,6 +14,7 @@ NetworkManager::NetworkManager(AOApplication *parent) : QObject(parent) server_socket = new QTcpSocket(this); http = new QNetworkAccessManager(this); + heartbeat_timer = new QTimer(this); connect(server_socket, SIGNAL(readyRead()), this, SLOT(handle_server_packet())); @@ -24,6 +25,9 @@ NetworkManager::NetworkManager(AOApplication *parent) : QObject(parent) ao_app->configini->value("master", "").value(); if (!master_config.isEmpty()) ms_baseurl = master_config; + + connect(heartbeat_timer, &QTimer::timeout, this, &NetworkManager::send_heartbeat); + heartbeat_timer->start(heartbeat_interval); } NetworkManager::~NetworkManager() {} @@ -69,12 +73,15 @@ void NetworkManager::ms_request_finished(QNetworkReply *reply, reply->deleteLater(); } -void NetworkManager::heartbeat_playing() +void NetworkManager::send_heartbeat() { // Ping the server periodically to tell the MS that you've been playing // within a 5 minute window, so that the the number of people playing within // that time period can be counted and an accurate player count be displayed. // What do I care about your personal information, I really don't want it. + if (ao_app->get_player_count_optout()) + return; + QNetworkRequest req(QUrl(ms_baseurl + "/playing")); req.setRawHeader("User-Agent", get_user_agent().toUtf8()); req.setTransferTimeout(timeout_milliseconds); -- cgit From cf12168ebd426d7658d7f7021c47be519caa215a Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Sat, 18 Dec 2021 22:22:30 -0600 Subject: Qt 5.9 compat; remove dead code --- src/networkmanager.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'src/networkmanager.cpp') diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp index f886c76..89a9bff 100644 --- a/src/networkmanager.cpp +++ b/src/networkmanager.cpp @@ -36,7 +36,6 @@ void NetworkManager::get_server_list(const std::function &cb) { QNetworkRequest req(QUrl(ms_baseurl + "/servers")); req.setRawHeader("User-Agent", get_user_agent().toUtf8()); - req.setTransferTimeout(timeout_milliseconds); QNetworkReply *reply = http->get(req); connect(reply, &QNetworkReply::finished, @@ -84,7 +83,6 @@ void NetworkManager::send_heartbeat() QNetworkRequest req(QUrl(ms_baseurl + "/playing")); req.setRawHeader("User-Agent", get_user_agent().toUtf8()); - req.setTransferTimeout(timeout_milliseconds); http->post(req, QByteArray()); } @@ -109,7 +107,6 @@ void NetworkManager::request_document(MSDocumentType document_type, language = QLocale::system().name(); req.setRawHeader("Accept-Language", language.toUtf8()); - req.setTransferTimeout(5000); qDebug().noquote().nospace() << "Getting " << endpoint << ", Accept-Language: " << language; @@ -160,7 +157,7 @@ void NetworkManager::handle_server_packet() } } - const QStringList packet_list = in_data.split("%", Qt::SkipEmptyParts); + const QStringList packet_list = in_data.split("%", QString::SkipEmptyParts); for (const QString &packet : packet_list) { AOPacket *f_packet = new AOPacket(packet); -- cgit From 5d79a83fb5a0f322271e5e3d6c37a8782d42a61a Mon Sep 17 00:00:00 2001 From: oldmud0 Date: Thu, 30 Dec 2021 21:26:28 -0600 Subject: Prevent old/bad "backup MS" values from preventing connection --- src/networkmanager.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/networkmanager.cpp') diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp index c1e8480..4c82385 100644 --- a/src/networkmanager.cpp +++ b/src/networkmanager.cpp @@ -23,15 +23,15 @@ NetworkManager::NetworkManager(AOApplication *parent) : QObject(parent) QString master_config = ao_app->configini->value("master", "").value(); - if (!master_config.isEmpty()) + if (!master_config.isEmpty() && QUrl(master_config).scheme().startsWith("http")) { + qInfo() << "using alternate master server" << master_config; ms_baseurl = master_config; + } connect(heartbeat_timer, &QTimer::timeout, this, &NetworkManager::send_heartbeat); heartbeat_timer->start(heartbeat_interval); } -NetworkManager::~NetworkManager() {} - void NetworkManager::get_server_list(const std::function &cb) { QNetworkRequest req(QUrl(ms_baseurl + "/servers")); @@ -120,7 +120,8 @@ void NetworkManager::request_document(MSDocumentType document_type, << "Failed to get " << endpoint << " (" << reply->errorString() << ") " << "(http status " << http_status << ")"; content = QString(); - } cb(content); + } + cb(content); reply->deleteLater(); }); } @@ -130,6 +131,9 @@ void NetworkManager::connect_to_server(server_type p_server) server_socket->close(); server_socket->abort(); + qInfo().nospace().noquote() << "connecting to " << p_server.ip << ":" + << p_server.port; + server_socket->connectToHost(p_server.ip, p_server.port); } -- cgit