From d8ec4ccdf5adfc392db9bfce841b82aeae56ddbb Mon Sep 17 00:00:00 2001
From: Salanto <62221668+Salanto@users.noreply.github.com>
Date: Sat, 20 Jul 2024 16:07:55 +0200
Subject: [PATCH] [Cleanup] Overhaul Server Advertiser/Publisher (#369)
* Overhaul advertiser to suck less
* Remove stray QDebug
---
bin/config_sample/config.ini | 3 -
core.pro | 4 +-
src/advertiser.cpp | 121 ------------------
src/advertiser.h | 112 ----------------
src/config_manager.cpp | 14 +-
src/config_manager.h | 13 +-
src/main.cpp | 2 +-
src/server.cpp | 14 +-
src/server.h | 9 +-
src/serverpublisher.cpp | 107 ++++++++++++++++
src/serverpublisher.h | 69 ++++++++++
.../tst_unittest_config_manager.cpp | 16 +--
12 files changed, 199 insertions(+), 285 deletions(-)
delete mode 100644 src/advertiser.cpp
delete mode 100644 src/advertiser.h
create mode 100644 src/serverpublisher.cpp
create mode 100644 src/serverpublisher.h
diff --git a/bin/config_sample/config.ini b/bin/config_sample/config.ini
index 8b7d490..4d3472f 100644
--- a/bin/config_sample/config.ini
+++ b/bin/config_sample/config.ini
@@ -61,9 +61,6 @@ asset_url=http://attorneyoffline.de/base/
; Whether or not the server should appear on the master server.
advertise=true
-; Wether the advertiser prints additional debug info
-debug=false
-
; The IP address of the master server. Unless something happens to the default one, you shouldn't change this.
ms_ip=https://servers.aceattorneyonline.com/servers
diff --git a/core.pro b/core.pro
index 186577d..0e939c2 100644
--- a/core.pro
+++ b/core.pro
@@ -51,8 +51,8 @@ SOURCES += \
src/packets.cpp \
src/playerstateobserver.cpp \
src/server.cpp \
+ src/serverpublisher.cpp \
src/testimony_recorder.cpp \
- src/advertiser.cpp \
src/logger/u_logger.cpp \
src/logger/writer_modcall.cpp \
src/logger/writer_full.cpp \
@@ -95,8 +95,8 @@ HEADERS += src/aoclient.h \
src/packet/packet_pr.h \
src/playerstateobserver.h \
src/server.h \
+ src/serverpublisher.h \
src/typedefs.h \
- src/advertiser.h \
src/logger/u_logger.h \
src/logger/writer_modcall.h \
src/logger/writer_full.h \
diff --git a/src/advertiser.cpp b/src/advertiser.cpp
deleted file mode 100644
index 0109e10..0000000
--- a/src/advertiser.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-//////////////////////////////////////////////////////////////////////////////////////
-// akashi - a server for Attorney Online 2 //
-// Copyright (C) 2020 scatterflower //
-// //
-// This program is free software: you can redistribute it and/or modify //
-// it under the terms of the GNU Affero General Public License as //
-// published by the Free Software Foundation, either version 3 of the //
-// License, or (at your option) any later version. //
-// //
-// This program is distributed in the hope that it will be useful, //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
-// GNU Affero General Public License for more details. //
-// //
-// You should have received a copy of the GNU Affero General Public License //
-// along with this program. If not, see . //
-//////////////////////////////////////////////////////////////////////////////////////
-#include "advertiser.h"
-
-#include "config_manager.h"
-
-Advertiser::Advertiser(int port)
-{
- m_manager = new QNetworkAccessManager();
- connect(m_manager, &QNetworkAccessManager::finished,
- this, &Advertiser::msRequestFinished);
-
- m_name = ConfigManager::serverName();
- m_hostname = ConfigManager::advertiserHostname();
- m_description = ConfigManager::serverDescription();
-
- // Cheap workaround to correctly advertise when Cloudflare tunnel is used.
- if (ConfigManager::advertiserCloudflareMode()) {
- m_port = 80;
- m_ws_port = 80;
- }
- else {
- m_port = port;
- m_ws_port = port;
- }
-
- m_masterserver = ConfigManager::advertiserIP();
- m_debug = ConfigManager::advertiserDebug();
-}
-
-Advertiser::~Advertiser()
-{
- m_manager->deleteLater();
-}
-
-void Advertiser::msAdvertiseServer()
-{
- if (m_masterserver.isValid()) {
-
- QUrl url(m_masterserver);
- QNetworkRequest request(url);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
-
- QJsonObject l_json;
-
- if (!m_hostname.isEmpty()) {
- l_json["ip"] = m_hostname;
- }
-
- l_json["port"] = m_port;
- if (m_ws_port != -1) {
- l_json["ws_port"] = m_ws_port;
- }
-
- l_json["players"] = m_players;
- l_json["name"] = m_name;
-
- if (!m_description.isEmpty()) {
- l_json["description"] = m_description;
- }
-
- m_manager->post(request, QJsonDocument(l_json).toJson());
-
- if (m_debug)
- qDebug().noquote() << "Advertised Server";
- return;
- }
- if (m_debug)
- qWarning().noquote() << "Unable to advertise. Masterserver URL '" + m_masterserver.toString() + "' is not valid.";
- return;
-}
-
-void Advertiser::msRequestFinished(QNetworkReply *f_reply)
-{
- if (m_debug) {
- if (f_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 200) {
- qDebug().noquote() << "Succesfully advertised server.";
- }
- else {
- QJsonDocument json = QJsonDocument::fromJson(f_reply->readAll());
- if (json.isNull()) {
- qCritical().noquote() << "Invalid JSON response from" << f_reply->url();
- f_reply->deleteLater();
- return;
- }
-
- qDebug().noquote() << "Got valid response from" << f_reply->url();
- qDebug() << json;
- }
- }
- f_reply->deleteLater();
-}
-
-void Advertiser::updatePlayerCount(int f_current_players)
-{
- m_players = f_current_players;
-}
-
-void Advertiser::updateAdvertiserSettings()
-{
- m_name = ConfigManager::serverName();
- m_hostname = ConfigManager::advertiserHostname();
- m_description = ConfigManager::serverDescription();
- m_masterserver = ConfigManager::advertiserIP();
- m_debug = ConfigManager::advertiserDebug();
-}
diff --git a/src/advertiser.h b/src/advertiser.h
deleted file mode 100644
index dc4c7e3..0000000
--- a/src/advertiser.h
+++ /dev/null
@@ -1,112 +0,0 @@
-//////////////////////////////////////////////////////////////////////////////////////
-// akashi - a server for Attorney Online 2 //
-// Copyright (C) 2020 scatterflower //
-// //
-// This program is free software: you can redistribute it and/or modify //
-// it under the terms of the GNU Affero General Public License as //
-// published by the Free Software Foundation, either version 3 of the //
-// License, or (at your option) any later version. //
-// //
-// This program is distributed in the hope that it will be useful, //
-// but WITHOUT ANY WARRANTY; without even the implied warranty of //
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
-// GNU Affero General Public License for more details. //
-// //
-// You should have received a copy of the GNU Affero General Public License //
-// along with this program. If not, see . //
-//////////////////////////////////////////////////////////////////////////////////////
-#ifndef ADVERTISER_H
-#define ADVERTISER_H
-
-#include
-#include
-
-/**
- * @brief Represents the advertiser of the server. Sends current server information to masterserver.
- */
-class Advertiser : public QObject
-{
- Q_OBJECT
-
- public:
- /**
- * @brief Constructor for the HTTP_Advertiser class.
- */
- explicit Advertiser(int port);
-
- /**
- * @brief Deconstructor for the HTTP_Advertiser class. Yes, that's it. Can't say more about it.
- */
- ~Advertiser();
-
- public slots:
-
- /**
- * @brief Establishes a connection with masterserver to register or update the listing on the masterserver.
- */
- void msAdvertiseServer();
-
- /**
- * @brief Reads the information send as a reply for further error handling.
- * @param reply Response data from the masterserver. Information contained is send to the console if debug is enabled.
- */
- void msRequestFinished(QNetworkReply *f_reply);
-
- /**
- * @brief Updates the playercount of the server in the advertiser.
- */
- void updatePlayerCount(int f_current_players);
-
- /**
- * @brief Updates advertisement values
- */
- void updateAdvertiserSettings();
-
- private:
- /**
- * @brief Pointer to the network manager, necessary to execute POST requests to the masterserver.
- */
- QNetworkAccessManager *m_manager;
-
- /**
- * @brief Name of the server send to the masterserver. Changing this will change the display name in the serverlist
- */
- QString m_name;
-
- /**
- * @brief Optional hostname of the server. Can either be an IP or a DNS name. Disabled automatic IP detection of ms3.
- */
- QString m_hostname;
-
- /**
- * @brief Description of the server that is displayed in the client when the server is selected.
- */
- QString m_description;
-
- /**
- * @brief Client port for the AO2-Client.
- */
- int m_port;
-
- /**
- * @brief Websocket proxy port for WebAO users.
- */
- int m_ws_port;
-
- /**
- * @brief Maximum amount of clients that can be connected to the server.
- */
- int m_players;
-
- /**
- * @brief URL of the masterserver that m_manager posts to. This is almost never changed.
- */
- QUrl m_masterserver;
-
- /**
- * @brief Controls if network replies are printed to console. Should only be true if issues communicating with masterserver appear.
- */
- bool m_debug;
-};
-
-#endif // ADVERTISER_H
diff --git a/src/config_manager.cpp b/src/config_manager.cpp
index 87ad9a5..bf8c60a 100644
--- a/src/config_manager.cpp
+++ b/src/config_manager.cpp
@@ -626,28 +626,22 @@ QStringList ConfigManager::cdnList()
return m_commands->cdns;
}
-bool ConfigManager::advertiseServer()
+bool ConfigManager::publishServerEnabled()
{
return m_settings->value("Advertiser/advertise", "true").toBool();
}
-bool ConfigManager::advertiserDebug()
+QUrl ConfigManager::serverlistURL()
{
- return m_settings->value("Advertiser/debug", "true").toBool();
-}
-
-QUrl ConfigManager::advertiserIP()
-{
- qDebug() << m_settings->value("Advertiser/ms_ip", "").toUrl();
return m_settings->value("Advertiser/ms_ip", "").toUrl();
}
-QString ConfigManager::advertiserHostname()
+QString ConfigManager::serverDomainName()
{
return m_settings->value("Advertiser/hostname", "").toString();
}
-bool ConfigManager::advertiserCloudflareMode()
+bool ConfigManager::advertiseWSProxy()
{
return m_settings->value("Advertiser/cloudflare_enabled", "false").toBool();
}
diff --git a/src/config_manager.h b/src/config_manager.h
index d8897db..2776354 100644
--- a/src/config_manager.h
+++ b/src/config_manager.h
@@ -431,29 +431,24 @@ class ConfigManager
/**
* @brief Returns if the advertiser is enabled to advertise on ms3.
*/
- static bool advertiseServer();
-
- /**
- * @brief Returns if the advertiser prints debug info to console.
- */
- static bool advertiserDebug();
+ static bool publishServerEnabled();
/**
* @brief Returns the IP or URL of the masterserver.
*/
- static QUrl advertiserIP();
+ static QUrl serverlistURL();
/**
* @brief Returns an optional hostname paramemter for the advertiser.
* If used allows user to set a custom IP or domain name.
*/
- static QString advertiserHostname();
+ static QString serverDomainName();
/**
* @brief Returns a dummy port instead of the real port
* @return
*/
- static bool advertiserCloudflareMode();
+ static bool advertiseWSProxy();
/**
* @brief Returns the uptime of the server in miliseconds.
diff --git a/src/main.cpp b/src/main.cpp
index 06f24cc..e38d7eb 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -45,7 +45,7 @@ int main(int argc, char *argv[])
QCoreApplication::quit();
}
else {
- server = new Server(ConfigManager::serverPort());
+ server = new Server(ConfigManager::serverPort(), &app);
server->start();
}
diff --git a/src/server.cpp b/src/server.cpp
index 554edc3..80db537 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -18,7 +18,6 @@
#include "server.h"
#include "acl_roles_handler.h"
-#include "advertiser.h"
#include "aoclient.h"
#include "area_data.h"
#include "command_extension.h"
@@ -29,6 +28,7 @@
#include "music_manager.h"
#include "network/network_socket.h"
#include "packet/packet_factory.h"
+#include "serverpublisher.h"
Server::Server(int p_ws_port, QObject *parent) :
QObject(parent),
@@ -81,17 +81,7 @@ void Server::start()
handleDiscordIntegration();
// Construct modern advertiser if enabled in config
- if (ConfigManager::advertiseServer()) {
- AdvertiserTimer = new QTimer(this);
- ms3_Advertiser = new Advertiser(server->serverPort());
-
- connect(AdvertiserTimer, &QTimer::timeout, ms3_Advertiser, &Advertiser::msAdvertiseServer);
- connect(this, &Server::playerCountUpdated, ms3_Advertiser, &Advertiser::updatePlayerCount);
- connect(this, &Server::updateHTTPConfiguration, ms3_Advertiser, &Advertiser::updateAdvertiserSettings);
- emit playerCountUpdated(m_player_count);
- ms3_Advertiser->msAdvertiseServer();
- AdvertiserTimer->start(300000);
- }
+ server_publisher = new ServerPublisher(server->serverPort(), &m_player_count, this);
// Get characters from config file
m_characters = ConfigManager::charlist();
diff --git a/src/server.h b/src/server.h
index 443253c..a0febe4 100644
--- a/src/server.h
+++ b/src/server.h
@@ -33,7 +33,7 @@
#include "playerstateobserver.h"
class ACLRolesHandler;
-class Advertiser;
+class ServerPublisher;
class AOClient;
class AreaData;
class CommandExtensionCollection;
@@ -421,12 +421,7 @@ class Server : public QObject
/**
* @brief Handles HTTP server advertising.
*/
- Advertiser *ms3_Advertiser;
-
- /**
- * @brief Advertises the server in a regular intervall.
- */
- QTimer *AdvertiserTimer;
+ ServerPublisher *server_publisher;
/**
* @brief Handles the universal log framework.
diff --git a/src/serverpublisher.cpp b/src/serverpublisher.cpp
new file mode 100644
index 0000000..4367248
--- /dev/null
+++ b/src/serverpublisher.cpp
@@ -0,0 +1,107 @@
+//////////////////////////////////////////////////////////////////////////////////////
+// akashi - a server for Attorney Online 2 //
+// Copyright (C) 2020 scatterflower //
+// //
+// This program is free software: you can redistribute it and/or modify //
+// it under the terms of the GNU Affero General Public License as //
+// published by the Free Software Foundation, either version 3 of the //
+// License, or (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU Affero General Public License for more details. //
+// //
+// You should have received a copy of the GNU Affero General Public License //
+// along with this program. If not, see . //
+//////////////////////////////////////////////////////////////////////////////////////
+#include "serverpublisher.h"
+#include "config_manager.h"
+#include "qnamespace.h"
+
+#include
+#include
+#include
+
+const int HTTP_OK = 200;
+const int WS_REVERSE_PROXY = 80;
+const int TIMEOUT = 1000 * 60 * 5;
+
+ServerPublisher::ServerPublisher(int port, int *player_count, QObject *parent) :
+ QObject(parent),
+ m_manager{new QNetworkAccessManager(this)},
+ timeout_timer(new QTimer(this)),
+ m_players(player_count),
+ m_port{port}
+{
+ connect(m_manager, &QNetworkAccessManager::finished, this, &ServerPublisher::finished);
+ connect(timeout_timer, &QTimer::timeout, this, &ServerPublisher::publishServer);
+
+ timeout_timer->setTimerType(Qt::PreciseTimer);
+ timeout_timer->setInterval(TIMEOUT);
+ timeout_timer->start();
+ publishServer();
+}
+
+void ServerPublisher::publishServer()
+{
+ if (!ConfigManager::publishServerEnabled()) {
+ return;
+ }
+
+ QUrl serverlist(ConfigManager::serverlistURL());
+ if (serverlist.isValid()) {
+ QNetworkRequest request(serverlist);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
+
+ QJsonObject serverinfo;
+ if (!ConfigManager::serverDomainName().trimmed().isEmpty()) {
+ serverinfo["ip"] = ConfigManager::serverDomainName();
+ }
+ serverinfo["port"] = 27106;
+ serverinfo["ws_port"] = ConfigManager::advertiseWSProxy() ? WS_REVERSE_PROXY : m_port;
+ serverinfo["players"] = *m_players;
+ serverinfo["name"] = ConfigManager::serverName();
+ serverinfo["description"] = ConfigManager::serverDescription();
+
+ m_manager->post(request, QJsonDocument(serverinfo).toJson());
+ }
+ else {
+ qWarning() << "Failed to advertise server. Serverlist URL is not valid. URL:" << serverlist.toString();
+ }
+}
+
+void ServerPublisher::finished(QNetworkReply *f_reply)
+{
+ QNetworkReply *reply(f_reply);
+ QString remote_url = reply->url().toString();
+
+ if (reply->error() != QNetworkReply::NoError) {
+ qWarning() << "Unable to connect to serverlist due to the following error:" << reply->errorString();
+ qWarning() << "Remote URL:" << remote_url;
+ }
+
+ QByteArray data = reply->isReadable() ? reply->readAll() : QByteArray();
+ const int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ if (status != HTTP_OK) {
+ QJsonParseError error;
+ QJsonDocument document = QJsonDocument::fromJson(data, &error);
+
+ if (error.error != QJsonParseError::NoError || !document.isObject()) {
+ qWarning() << "Received malformed response from masterserver. Error:" << error.errorString();
+ return;
+ }
+
+ QJsonObject body = document.object();
+ if (body.contains("errors")) {
+ qWarning() << "Failed to advertise to the serverlist due to the following errors:";
+ const QJsonArray errors = body["errors"].toArray();
+ for (const auto &ref : errors) {
+ QJsonObject error = ref.toObject();
+ qWarning().noquote() << "Error:" << error["type"].toString() << ". Message:" << error["message"].toString();
+ }
+ return;
+ }
+ }
+ qInfo() << "Sucessfully advertised server to serverlist.";
+}
diff --git a/src/serverpublisher.h b/src/serverpublisher.h
new file mode 100644
index 0000000..b0a1b16
--- /dev/null
+++ b/src/serverpublisher.h
@@ -0,0 +1,69 @@
+//////////////////////////////////////////////////////////////////////////////////////
+// akashi - a server for Attorney Online 2 //
+// Copyright (C) 2020 scatterflower //
+// //
+// This program is free software: you can redistribute it and/or modify //
+// it under the terms of the GNU Affero General Public License as //
+// published by the Free Software Foundation, either version 3 of the //
+// License, or (at your option) any later version. //
+// //
+// This program is distributed in the hope that it will be useful, //
+// but WITHOUT ANY WARRANTY; without even the implied warranty of //
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
+// GNU Affero General Public License for more details. //
+// //
+// You should have received a copy of the GNU Affero General Public License //
+// along with this program. If not, see . //
+//////////////////////////////////////////////////////////////////////////////////////
+#pragma once
+
+#include
+
+class QNetworkAccessManager;
+class QNetworkReply;
+class QTimer;
+
+/**
+ * @brief Represents the ServerPublisher of the server. Sends current server information to the serverlist.
+ */
+class ServerPublisher : public QObject
+{
+ Q_OBJECT
+
+ public:
+ explicit ServerPublisher(int port, int *player_count, QObject *parent = nullptr);
+ virtual ~ServerPublisher(){};
+
+ public slots:
+
+ /**
+ * @brief Establishes a connection with masterserver to register or update the listing on the masterserver.
+ */
+ void publishServer();
+
+ /**
+ * @brief Reads the response from the serverlist.
+ */
+ void finished(QNetworkReply *f_reply);
+
+ private:
+ /**
+ * @brief Pointer to the network manager, necessary to execute POST requests to the masterserver.
+ */
+ QNetworkAccessManager *m_manager;
+
+ /**
+ * @brief Advertisers when it expires.
+ */
+ QTimer *timeout_timer;
+
+ /**
+ * @brief The current amount of players on the server.
+ */
+ int *m_players;
+
+ /**
+ * @brief The WS port of the server.
+ */
+ int m_port;
+};
diff --git a/tests/unittest_config_manager/tst_unittest_config_manager.cpp b/tests/unittest_config_manager/tst_unittest_config_manager.cpp
index 7737c3b..b0eeee8 100644
--- a/tests/unittest_config_manager/tst_unittest_config_manager.cpp
+++ b/tests/unittest_config_manager/tst_unittest_config_manager.cpp
@@ -130,15 +130,15 @@ class tst_ConfigManager : public QObject
void cdnList();
- void advertiseServer();
+ void publishServerEnabled();
void advertiserDebug();
- void advertiserIP();
+ void serverlistURL();
- void advertiserHostname();
+ void serverDomainName();
- void advertiserCloudflareMode();
+ void advertiseWSProxy();
};
void tst_ConfigManager::verifyServerConfig()
@@ -426,7 +426,7 @@ void tst_ConfigManager::cdnList()
{
}
-void tst_ConfigManager::advertiseServer()
+void tst_ConfigManager::publishServerEnabled()
{
}
@@ -434,15 +434,15 @@ void tst_ConfigManager::advertiserDebug()
{
}
-void tst_ConfigManager::advertiserIP()
+void tst_ConfigManager::serverlistURL()
{
}
-void tst_ConfigManager::advertiserHostname()
+void tst_ConfigManager::serverDomainName()
{
}
-void tst_ConfigManager::advertiserCloudflareMode()
+void tst_ConfigManager::advertiseWSProxy()
{
}