Merge pull request #619 from AttorneyOnline/feature/http-ms
Switch to HTTP-based server list
This commit is contained in:
commit
c80a019574
@ -1 +1 @@
|
||||
Subproject commit 0d8220b1912e841ecaaa8f47198c8406e41fb5b4
|
||||
Subproject commit 6fff4608360e91d86edac28c42c546ee31739f97
|
@ -75,10 +75,8 @@ public:
|
||||
void construct_courtroom();
|
||||
void destruct_courtroom();
|
||||
|
||||
void ms_packet_received(AOPacket *p_packet);
|
||||
void server_packet_received(AOPacket *p_packet);
|
||||
|
||||
void send_ms_packet(AOPacket *p_packet);
|
||||
void send_server_packet(AOPacket *p_packet, bool encoded = true);
|
||||
|
||||
void call_settings_menu();
|
||||
@ -136,7 +134,7 @@ public:
|
||||
QVector<server_type> &get_favorite_list() { return favorite_list; }
|
||||
void add_favorite_server(int p_server);
|
||||
|
||||
void set_server_list();
|
||||
void set_server_list(QVector<server_type> &servers) { server_list = servers; }
|
||||
QVector<server_type> &get_server_list() { return server_list; }
|
||||
|
||||
// reads the theme from config.ini and sets it accordingly
|
||||
@ -522,6 +520,9 @@ public:
|
||||
// Get a list of custom mount paths
|
||||
QStringList get_mount_paths();
|
||||
|
||||
// Get whether to opt out of player count metrics sent to the master server
|
||||
bool get_player_count_optout();
|
||||
|
||||
// Currently defined subtheme
|
||||
QString subtheme;
|
||||
|
||||
@ -561,9 +562,6 @@ private:
|
||||
QHash<uint, QString> dir_listing_cache;
|
||||
QSet<uint> dir_listing_exist_cache;
|
||||
|
||||
private slots:
|
||||
void ms_connect_finished(bool connected, bool will_retry);
|
||||
|
||||
public slots:
|
||||
void server_disconnected();
|
||||
void loading_cancelled();
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include <QDirIterator>
|
||||
#include <QListWidget>
|
||||
#include <QTextBrowser>
|
||||
#include <QTextStream>
|
||||
|
||||
class Lobby;
|
||||
@ -192,6 +193,12 @@ private:
|
||||
QPushButton *ui_mount_down;
|
||||
QPushButton *ui_mount_clear_cache;
|
||||
|
||||
QWidget *ui_privacy_tab;
|
||||
QVBoxLayout *ui_privacy_layout;
|
||||
QCheckBox *ui_privacy_optout_cb;
|
||||
QFrame *ui_privacy_separator;
|
||||
QTextBrowser *ui_privacy_policy;
|
||||
|
||||
bool asset_cache_dirty = false;
|
||||
|
||||
bool needs_default_audiodev();
|
||||
|
@ -30,6 +30,8 @@ public:
|
||||
void set_widgets();
|
||||
void list_servers();
|
||||
void list_favorites();
|
||||
void get_motd();
|
||||
void check_for_updates();
|
||||
void append_chatmessage(QString f_name, QString f_message);
|
||||
void append_error(QString f_message);
|
||||
void set_player_count(int players_online, int max_players);
|
||||
@ -76,9 +78,6 @@ private:
|
||||
|
||||
AOTextArea *ui_chatbox;
|
||||
|
||||
QLineEdit *ui_chatname;
|
||||
QLineEdit *ui_chatmessage;
|
||||
|
||||
AOImage *ui_loading_background;
|
||||
QTextEdit *ui_loading_text;
|
||||
QProgressBar *ui_progress_bar;
|
||||
@ -103,7 +102,6 @@ private slots:
|
||||
void on_server_list_clicked(QTreeWidgetItem *p_item, int column);
|
||||
void on_server_list_doubleclicked(QTreeWidgetItem *p_item, int column);
|
||||
void on_server_search_edited(QString p_text);
|
||||
void on_chatfield_return_pressed();
|
||||
};
|
||||
|
||||
#endif // LOBBY_H
|
||||
|
@ -1,83 +1,64 @@
|
||||
#ifndef NETWORKMANAGER_H
|
||||
#define NETWORKMANAGER_H
|
||||
|
||||
// Qt for Android has stubbed QDnsLookup. This is not documented in any part of
|
||||
// their wiki. This prevents SRV lookup/failover behavior from functioning.
|
||||
// https://bugreports.qt.io/browse/QTBUG-56143
|
||||
#ifndef ANDROID
|
||||
#define MS_FAILOVER_SUPPORTED
|
||||
#endif
|
||||
|
||||
//#define LOCAL_MS
|
||||
|
||||
#ifdef LOCAL_MS
|
||||
#undef MS_FAILOVER_SUPPORTED
|
||||
#endif
|
||||
|
||||
#include "aoapplication.h"
|
||||
#include "aopacket.h"
|
||||
|
||||
#include <QDnsLookup>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QTcpSocket>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
enum MSDocumentType {
|
||||
PrivacyPolicy,
|
||||
Motd,
|
||||
ClientVersion
|
||||
};
|
||||
|
||||
class NetworkManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
NetworkManager(AOApplication *parent);
|
||||
~NetworkManager();
|
||||
explicit NetworkManager(AOApplication *parent);
|
||||
~NetworkManager() = default;
|
||||
|
||||
AOApplication *ao_app;
|
||||
QTcpSocket *ms_socket;
|
||||
QNetworkAccessManager *http;
|
||||
QTcpSocket *server_socket;
|
||||
QDnsLookup *ms_dns;
|
||||
QTimer *ms_reconnect_timer;
|
||||
QTimer *heartbeat_timer;
|
||||
|
||||
const QString ms_srv_hostname = "_aoms._tcp.aceattorneyonline.com";
|
||||
#ifdef LOCAL_MS
|
||||
QString ms_nosrv_hostname = "localhost";
|
||||
#else
|
||||
QString ms_nosrv_hostname = "master.aceattorneyonline.com";
|
||||
#endif
|
||||
const QString DEFAULT_MS_BASEURL = "https://servers.aceattorneyonline.com";
|
||||
QString ms_baseurl = DEFAULT_MS_BASEURL;
|
||||
|
||||
const quint16 ms_port = 27016;
|
||||
const int timeout_milliseconds = 2000;
|
||||
|
||||
// in seconds
|
||||
const int ms_reconnect_delay = 7;
|
||||
|
||||
bool ms_partial_packet = false;
|
||||
QString ms_temp_packet = "";
|
||||
const int heartbeat_interval = 60 * 5;
|
||||
|
||||
bool partial_packet = false;
|
||||
QString temp_packet = "";
|
||||
|
||||
unsigned int s_decryptor = 5;
|
||||
|
||||
void connect_to_master();
|
||||
void connect_to_master_nosrv();
|
||||
void connect_to_server(server_type p_server);
|
||||
|
||||
public slots:
|
||||
void ship_ms_packet(QString p_packet);
|
||||
void get_server_list(const std::function<void()> &cb);
|
||||
void ship_server_packet(QString p_packet);
|
||||
void handle_server_packet();
|
||||
|
||||
signals:
|
||||
void ms_connect_finished(bool success, bool will_retry);
|
||||
void request_document(MSDocumentType document_type,
|
||||
const std::function<void(QString)> &cb);
|
||||
void send_heartbeat();
|
||||
private slots:
|
||||
void ms_request_finished(QNetworkReply *reply,
|
||||
const std::function<void()> &cb);
|
||||
|
||||
private:
|
||||
void perform_srv_lookup();
|
||||
|
||||
private slots:
|
||||
void on_srv_lookup();
|
||||
void handle_ms_packet();
|
||||
void handle_server_packet();
|
||||
void on_ms_nosrv_connect_success();
|
||||
void on_ms_socket_error(QAbstractSocket::SocketError error);
|
||||
void retry_ms_connect();
|
||||
QString get_user_agent() const {
|
||||
return QStringLiteral("AttorneyOnline/%1 (Desktop)")
|
||||
.arg(ao_app->get_version_string());
|
||||
}
|
||||
};
|
||||
|
||||
#endif // NETWORKMANAGER_H
|
||||
|
@ -25,8 +25,6 @@ AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv)
|
||||
|
||||
net_manager = new NetworkManager(this);
|
||||
discord = new AttorneyOnline::Discord();
|
||||
connect(net_manager, &NetworkManager::ms_connect_finished,
|
||||
this, &AOApplication::ms_connect_finished);
|
||||
|
||||
asset_lookup_cache.reserve(2048);
|
||||
|
||||
@ -167,33 +165,6 @@ void AOApplication::loading_cancelled()
|
||||
w_lobby->hide_loading_overlay();
|
||||
}
|
||||
|
||||
void AOApplication::ms_connect_finished(bool connected, bool will_retry)
|
||||
{
|
||||
if (connected) {
|
||||
AOPacket *f_packet = new AOPacket("ALL#%");
|
||||
send_ms_packet(f_packet);
|
||||
}
|
||||
else {
|
||||
if (will_retry) {
|
||||
if (lobby_constructed)
|
||||
w_lobby->append_error(
|
||||
tr("Error connecting to master server. Will try again in %1 "
|
||||
"seconds.")
|
||||
.arg(QString::number(net_manager->ms_reconnect_delay)));
|
||||
}
|
||||
else {
|
||||
call_error(tr("There was an error connecting to the master server.\n"
|
||||
"We deploy multiple master servers to mitigate any "
|
||||
"possible downtime, "
|
||||
"but the client appears to have exhausted all possible "
|
||||
"methods of finding "
|
||||
"and connecting to one.\n"
|
||||
"Please check your Internet connection and firewall, and "
|
||||
"please try again."));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AOApplication::call_settings_menu()
|
||||
{
|
||||
AOOptionsDialog settings(nullptr, this);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "courtroom.h"
|
||||
#include "lobby.h"
|
||||
#include "bass.h"
|
||||
#include "networkmanager.h"
|
||||
|
||||
#include <QFileDialog>
|
||||
|
||||
@ -381,10 +382,9 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
|
||||
|
||||
row += 1;
|
||||
ui_ms_lbl = new QLabel(ui_form_layout_widget);
|
||||
ui_ms_lbl->setText(tr("Backup MS:"));
|
||||
ui_ms_lbl->setText(tr("Alternate Server List:"));
|
||||
ui_ms_lbl->setToolTip(
|
||||
tr("If the built-in server lookups fail, the game will try the "
|
||||
"address given here and use it as a backup master server address."));
|
||||
tr("Overrides the base URL to retrieve server information from."));
|
||||
|
||||
ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_ms_lbl);
|
||||
|
||||
@ -1067,6 +1067,30 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
|
||||
ui_mount_down->setEnabled(false);
|
||||
});
|
||||
|
||||
// Privacy tab
|
||||
ui_privacy_tab = new QWidget(this);
|
||||
ui_settings_tabs->addTab(ui_privacy_tab, tr("Privacy"));
|
||||
|
||||
ui_privacy_layout = new QVBoxLayout(ui_privacy_tab);
|
||||
|
||||
ui_privacy_optout_cb = new QCheckBox(ui_privacy_tab);
|
||||
ui_privacy_optout_cb->setText(tr("Do not include me in public player counts"));
|
||||
ui_privacy_layout->addWidget(ui_privacy_optout_cb);
|
||||
|
||||
ui_privacy_separator = new QFrame(ui_privacy_tab);
|
||||
ui_privacy_separator->setObjectName(QString::fromUtf8("line"));
|
||||
ui_privacy_separator->setFrameShape(QFrame::HLine);
|
||||
ui_privacy_separator->setFrameShadow(QFrame::Sunken);
|
||||
ui_privacy_layout->addWidget(ui_privacy_separator);
|
||||
|
||||
ui_privacy_policy = new QTextBrowser(ui_privacy_tab);
|
||||
QSizePolicy privacySizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
sizePolicy.setHorizontalStretch(0);
|
||||
sizePolicy.setVerticalStretch(0);
|
||||
ui_privacy_policy->setSizePolicy(privacySizePolicy);
|
||||
ui_privacy_policy->setPlainText(tr("Getting privacy policy..."));
|
||||
ui_privacy_layout->addWidget(ui_privacy_policy);
|
||||
|
||||
update_values();
|
||||
|
||||
// When we're done, we should continue the updates!
|
||||
@ -1150,6 +1174,15 @@ void AOOptionsDialog::update_values() {
|
||||
defaultMount->setFlags(Qt::ItemFlag::NoItemFlags);
|
||||
ui_mount_list->addItem(defaultMount);
|
||||
ui_mount_list->addItems(ao_app->get_mount_paths());
|
||||
|
||||
ui_privacy_optout_cb->setChecked(ao_app->get_player_count_optout());
|
||||
|
||||
ao_app->net_manager->request_document(MSDocumentType::PrivacyPolicy, [this](QString document) {
|
||||
if (document.isEmpty()) {
|
||||
document = tr("Couldn't get the privacy policy.");
|
||||
}
|
||||
ui_privacy_policy->setHtml(document);
|
||||
});
|
||||
}
|
||||
|
||||
void AOOptionsDialog::save_pressed()
|
||||
@ -1225,6 +1258,7 @@ void AOOptionsDialog::save_pressed()
|
||||
configini->setValue("casing_cm_enabled", ui_casing_cm_cb->isChecked());
|
||||
configini->setValue("casing_can_host_cases",
|
||||
ui_casing_cm_cases_textbox->text());
|
||||
configini->setValue("player_count_optout", ui_privacy_optout_cb->isChecked());
|
||||
|
||||
QStringList mountPaths;
|
||||
for (int i = 1; i < ui_mount_list->count(); i++)
|
||||
@ -1237,8 +1271,6 @@ void AOOptionsDialog::save_pressed()
|
||||
if (asset_cache_dirty)
|
||||
ao_app->invalidate_lookup_cache();
|
||||
|
||||
callwordsini->close();
|
||||
|
||||
// We most probably pressed "Restore defaults" at some point. Since we're saving our settings, remove the temporary file.
|
||||
if (QFile::exists(ao_app->get_base_path() + "config.temp"))
|
||||
QFile::remove(ao_app->get_base_path() + "config.temp");
|
||||
|
@ -981,7 +981,7 @@ void Courtroom::set_widgets()
|
||||
|
||||
set_size_and_pos(ui_settings, "settings");
|
||||
ui_settings->setText(tr("Settings"));
|
||||
ui_settings->set_image("settings");
|
||||
ui_settings->set_image("courtroom_settings");
|
||||
ui_settings->setToolTip(
|
||||
tr("Allows you to change various aspects of the client."));
|
||||
|
||||
@ -4199,8 +4199,6 @@ void Courtroom::on_ooc_return_pressed()
|
||||
|
||||
if (server_ooc)
|
||||
ao_app->send_server_packet(f_packet);
|
||||
else
|
||||
ao_app->send_ms_packet(f_packet);
|
||||
|
||||
ui_ooc_chat_message->clear();
|
||||
|
||||
|
@ -59,12 +59,6 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow()
|
||||
ui_chatbox = new AOTextArea(this);
|
||||
ui_chatbox->setOpenExternalLinks(true);
|
||||
ui_chatbox->setObjectName("ui_chatbox");
|
||||
ui_chatname = new QLineEdit(this);
|
||||
ui_chatname->setPlaceholderText(tr("Name"));
|
||||
ui_chatname->setText(ao_app->get_ooc_name());
|
||||
ui_chatname->setObjectName("ui_chatname");
|
||||
ui_chatmessage = new QLineEdit(this);
|
||||
ui_chatmessage->setObjectName("ui_chatmessage");
|
||||
ui_loading_background = new AOImage(this, ao_app);
|
||||
ui_loading_background->setObjectName("ui_loading_background");
|
||||
ui_loading_text = new QTextEdit(ui_loading_background);
|
||||
@ -95,13 +89,13 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow()
|
||||
this, &Lobby::on_server_list_doubleclicked);
|
||||
connect(ui_server_search, &QLineEdit::textChanged, this,
|
||||
&Lobby::on_server_search_edited);
|
||||
connect(ui_chatmessage, &QLineEdit::returnPressed, this,
|
||||
&Lobby::on_chatfield_return_pressed);
|
||||
connect(ui_cancel, &AOButton::clicked, ao_app, &AOApplication::loading_cancelled);
|
||||
|
||||
ui_connect->setEnabled(false);
|
||||
|
||||
list_servers();
|
||||
get_motd();
|
||||
check_for_updates();
|
||||
|
||||
set_widgets();
|
||||
}
|
||||
@ -156,7 +150,7 @@ void Lobby::set_widgets()
|
||||
|
||||
set_size_and_pos(ui_settings, "settings");
|
||||
ui_settings->setText(tr("Settings"));
|
||||
ui_settings->set_image("settings");
|
||||
ui_settings->set_image("lobby_settings");
|
||||
ui_settings->setToolTip(
|
||||
tr("Allows you to change various aspects of the client."));
|
||||
|
||||
@ -173,10 +167,6 @@ void Lobby::set_widgets()
|
||||
set_size_and_pos(ui_chatbox, "chatbox");
|
||||
ui_chatbox->setReadOnly(true);
|
||||
|
||||
set_size_and_pos(ui_chatname, "chatname");
|
||||
|
||||
set_size_and_pos(ui_chatmessage, "chatmessage");
|
||||
|
||||
ui_loading_background->resize(this->width(), this->height());
|
||||
ui_loading_background->set_image("loadingbackground");
|
||||
|
||||
@ -219,8 +209,6 @@ void Lobby::set_fonts()
|
||||
set_font(ui_player_count, "player_count");
|
||||
set_font(ui_description, "description");
|
||||
set_font(ui_chatbox, "chatbox");
|
||||
set_font(ui_chatname, "chatname");
|
||||
set_font(ui_chatmessage, "chatmessage");
|
||||
set_font(ui_loading_text, "loading_text");
|
||||
set_font(ui_server_list, "server_list");
|
||||
}
|
||||
@ -331,9 +319,13 @@ void Lobby::on_refresh_released()
|
||||
{
|
||||
ui_refresh->set_image("refresh");
|
||||
|
||||
AOPacket *f_packet = new AOPacket("ALL#%");
|
||||
|
||||
ao_app->send_ms_packet(f_packet);
|
||||
if (public_servers_selected) {
|
||||
ao_app->net_manager->get_server_list(std::bind(&Lobby::list_servers, this));
|
||||
get_motd();
|
||||
} else {
|
||||
ao_app->set_favorite_list();
|
||||
list_favorites();
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::on_add_to_fav_pressed()
|
||||
@ -490,22 +482,6 @@ void Lobby::on_server_search_edited(QString p_text)
|
||||
}
|
||||
}
|
||||
|
||||
void Lobby::on_chatfield_return_pressed()
|
||||
{
|
||||
// no you can't send empty messages
|
||||
if (ui_chatname->text() == "" || ui_chatmessage->text() == "")
|
||||
return;
|
||||
|
||||
QString f_header = "CT";
|
||||
QStringList f_contents{ui_chatname->text(), ui_chatmessage->text()};
|
||||
|
||||
AOPacket *f_packet = new AOPacket(f_header, f_contents);
|
||||
|
||||
ao_app->send_ms_packet(f_packet);
|
||||
|
||||
ui_chatmessage->clear();
|
||||
}
|
||||
|
||||
void Lobby::list_servers()
|
||||
{
|
||||
public_servers_selected = true;
|
||||
@ -544,6 +520,29 @@ void Lobby::list_favorites()
|
||||
ui_server_list->setSortingEnabled(true);
|
||||
}
|
||||
|
||||
void Lobby::get_motd()
|
||||
{
|
||||
ao_app->net_manager->request_document(MSDocumentType::Motd,
|
||||
[this](QString document) {
|
||||
if (document.isEmpty()) {
|
||||
document = tr("Couldn't get the message of the day.");
|
||||
}
|
||||
ui_chatbox->setHtml(document);
|
||||
});
|
||||
}
|
||||
|
||||
void Lobby::check_for_updates()
|
||||
{
|
||||
ao_app->net_manager->request_document(MSDocumentType::ClientVersion,
|
||||
[this](QString version) {
|
||||
const QString current_version = ao_app->get_version_string();
|
||||
if (!version.isEmpty() && version != current_version) {
|
||||
ui_version->setText(tr("Version: %1 (!)").arg(current_version));
|
||||
ui_version->setToolTip(tr("New version available: %1").arg(version));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void Lobby::append_chatmessage(QString f_name, QString f_message)
|
||||
{
|
||||
ui_chatbox->append_chatmessage(
|
||||
|
@ -57,7 +57,8 @@ int main(int argc, char *argv[])
|
||||
main_app.installTranslator(&appTranslator);
|
||||
|
||||
main_app.construct_lobby();
|
||||
main_app.net_manager->connect_to_master();
|
||||
main_app.net_manager->get_server_list(std::bind(&Lobby::list_servers, main_app.w_lobby));
|
||||
main_app.net_manager->send_heartbeat();
|
||||
main_app.w_lobby->show();
|
||||
return main_app.exec();
|
||||
}
|
||||
|
@ -4,59 +4,126 @@
|
||||
#include "debug_functions.h"
|
||||
#include "lobby.h"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QNetworkReply>
|
||||
|
||||
NetworkManager::NetworkManager(AOApplication *parent) : QObject(parent)
|
||||
{
|
||||
ao_app = parent;
|
||||
|
||||
ms_socket = new QTcpSocket(this);
|
||||
server_socket = new QTcpSocket(this);
|
||||
http = new QNetworkAccessManager(this);
|
||||
heartbeat_timer = new QTimer(this);
|
||||
|
||||
ms_reconnect_timer = new QTimer(this);
|
||||
ms_reconnect_timer->setSingleShot(true);
|
||||
connect(ms_reconnect_timer, &QTimer::timeout, this,
|
||||
&NetworkManager::retry_ms_connect);
|
||||
|
||||
connect(ms_socket, &QTcpSocket::readyRead, this,
|
||||
&NetworkManager::handle_ms_packet);
|
||||
connect(server_socket, &QTcpSocket::readyRead, this,
|
||||
&NetworkManager::handle_server_packet);
|
||||
&NetworkManager::handle_server_packet);
|
||||
connect(server_socket, &QTcpSocket::disconnected, ao_app,
|
||||
&AOApplication::server_disconnected);
|
||||
&AOApplication::server_disconnected);
|
||||
|
||||
QString master_config =
|
||||
ao_app->configini->value("master", "").value<QString>();
|
||||
if (master_config != "")
|
||||
ms_nosrv_hostname = master_config;
|
||||
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::connect_to_master()
|
||||
void NetworkManager::get_server_list(const std::function<void()> &cb)
|
||||
{
|
||||
ms_socket->close();
|
||||
ms_socket->abort();
|
||||
QNetworkRequest req(QUrl(ms_baseurl + "/servers"));
|
||||
req.setRawHeader("User-Agent", get_user_agent().toUtf8());
|
||||
|
||||
#ifdef MS_FAILOVER_SUPPORTED
|
||||
perform_srv_lookup();
|
||||
#else
|
||||
connect_to_master_nosrv();
|
||||
#endif
|
||||
QNetworkReply *reply = http->get(req);
|
||||
connect(reply, &QNetworkReply::finished,
|
||||
this, std::bind(&NetworkManager::ms_request_finished, this, reply, cb));
|
||||
}
|
||||
|
||||
void NetworkManager::connect_to_master_nosrv()
|
||||
void NetworkManager::ms_request_finished(QNetworkReply *reply,
|
||||
const std::function<void()> &cb)
|
||||
{
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(ms_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#else
|
||||
connect(ms_socket, &QTcpSocket::errorOccurred,
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#endif
|
||||
QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
|
||||
if (json.isNull()) {
|
||||
qCritical().noquote() << "Invalid JSON response from" << reply->url();
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
connect(ms_socket, &QTcpSocket::connected,
|
||||
this, &NetworkManager::on_ms_nosrv_connect_success);
|
||||
qDebug().noquote() << "Got valid response from" << reply->url();
|
||||
|
||||
ms_socket->connectToHost(ms_nosrv_hostname, ms_port);
|
||||
QVector<server_type> 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);
|
||||
|
||||
cb();
|
||||
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
http->post(req, QByteArray());
|
||||
}
|
||||
|
||||
void NetworkManager::request_document(MSDocumentType document_type,
|
||||
const std::function<void(QString)> &cb)
|
||||
{
|
||||
const QMap<MSDocumentType, QString> 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());
|
||||
|
||||
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::connect_to_server(server_type p_server)
|
||||
@ -64,17 +131,10 @@ void NetworkManager::connect_to_server(server_type p_server)
|
||||
server_socket->close();
|
||||
server_socket->abort();
|
||||
|
||||
server_socket->connectToHost(p_server.ip, p_server.port);
|
||||
}
|
||||
qInfo().nospace().noquote() << "connecting to " << p_server.ip << ":"
|
||||
<< p_server.port;
|
||||
|
||||
void NetworkManager::ship_ms_packet(QString p_packet)
|
||||
{
|
||||
if (!ms_socket->isOpen()) {
|
||||
retry_ms_connect();
|
||||
}
|
||||
else {
|
||||
ms_socket->write(p_packet.toUtf8());
|
||||
}
|
||||
server_socket->connectToHost(p_server.ip, p_server.port);
|
||||
}
|
||||
|
||||
void NetworkManager::ship_server_packet(QString p_packet)
|
||||
@ -82,152 +142,6 @@ void NetworkManager::ship_server_packet(QString p_packet)
|
||||
server_socket->write(p_packet.toUtf8());
|
||||
}
|
||||
|
||||
void NetworkManager::handle_ms_packet()
|
||||
{
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
else {
|
||||
if (ms_partial_packet) {
|
||||
in_data = ms_temp_packet + in_data;
|
||||
ms_temp_packet = "";
|
||||
ms_partial_packet = false;
|
||||
}
|
||||
}
|
||||
|
||||
QStringList packet_list =
|
||||
in_data.split("%", QString::SplitBehavior(QString::SkipEmptyParts));
|
||||
|
||||
for (QString packet : packet_list) {
|
||||
AOPacket *f_packet = new AOPacket(packet);
|
||||
|
||||
ao_app->ms_packet_received(f_packet);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::perform_srv_lookup()
|
||||
{
|
||||
#ifdef MS_FAILOVER_SUPPORTED
|
||||
ms_dns = new QDnsLookup(QDnsLookup::SRV, ms_srv_hostname, this);
|
||||
|
||||
connect(ms_dns, &QDnsLookup::finished, this, &NetworkManager::on_srv_lookup);
|
||||
ms_dns->lookup();
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkManager::on_srv_lookup()
|
||||
{
|
||||
#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();
|
||||
qCritical() << "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
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(ms_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#else
|
||||
connect(ms_socket, &QTcpSocket::errorOccurred,
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#endif
|
||||
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
|
||||
}
|
||||
|
||||
void NetworkManager::on_ms_nosrv_connect_success()
|
||||
{
|
||||
emit ms_connect_finished(true, false);
|
||||
|
||||
disconnect(ms_socket, &QTcpSocket::connected, this,
|
||||
&NetworkManager::on_ms_nosrv_connect_success);
|
||||
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
connect(ms_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#else
|
||||
connect(ms_socket, &QTcpSocket::errorOccurred,
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NetworkManager::on_ms_socket_error(QAbstractSocket::SocketError error)
|
||||
{
|
||||
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
|
||||
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
|
||||
disconnect(ms_socket, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#else
|
||||
disconnect(ms_socket, &QTcpSocket::errorOccurred,
|
||||
this, &NetworkManager::on_ms_socket_error);
|
||||
#endif
|
||||
|
||||
emit ms_connect_finished(false, true);
|
||||
|
||||
ms_reconnect_timer->start(ms_reconnect_delay * 1000);
|
||||
}
|
||||
|
||||
void NetworkManager::retry_ms_connect()
|
||||
{
|
||||
if (!ms_reconnect_timer->isActive() &&
|
||||
ms_socket->state() != QAbstractSocket::ConnectingState)
|
||||
connect_to_master();
|
||||
}
|
||||
|
||||
void NetworkManager::handle_server_packet()
|
||||
{
|
||||
QByteArray buffer = server_socket->readAll();
|
||||
@ -247,10 +161,9 @@ void NetworkManager::handle_server_packet()
|
||||
}
|
||||
}
|
||||
|
||||
QStringList packet_list =
|
||||
in_data.split("%", QString::SplitBehavior(QString::SkipEmptyParts));
|
||||
const QStringList packet_list = in_data.split("%", QString::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);
|
||||
|
@ -6,106 +6,6 @@
|
||||
#include "lobby.h"
|
||||
#include "networkmanager.h"
|
||||
|
||||
void AOApplication::ms_packet_received(AOPacket *p_packet)
|
||||
{
|
||||
QString header = p_packet->get_header();
|
||||
|
||||
// Some packets need to handle decode/encode separately
|
||||
if (header != "SC") {
|
||||
p_packet->net_decode();
|
||||
}
|
||||
QStringList f_contents = p_packet->get_contents();
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
if (header != "CHECK")
|
||||
qDebug() << "R(ms):" << p_packet->to_string();
|
||||
#endif
|
||||
|
||||
if (header == "ALL") {
|
||||
server_list.clear();
|
||||
|
||||
for (QString i_string : p_packet->get_contents()) {
|
||||
server_type f_server;
|
||||
QStringList sub_contents = i_string.split("&");
|
||||
|
||||
if (sub_contents.size() < 4) {
|
||||
qWarning() << "malformed packet";
|
||||
continue;
|
||||
}
|
||||
|
||||
f_server.name = sub_contents.at(0);
|
||||
f_server.desc = sub_contents.at(1);
|
||||
f_server.ip = sub_contents.at(2);
|
||||
f_server.port = sub_contents.at(3).toInt();
|
||||
|
||||
server_list.append(f_server);
|
||||
}
|
||||
|
||||
if (lobby_constructed) {
|
||||
w_lobby->list_servers();
|
||||
}
|
||||
}
|
||||
else if (header == "CT") {
|
||||
QString f_name, f_message;
|
||||
|
||||
if (f_contents.size() == 1) {
|
||||
f_name = "";
|
||||
f_message = f_contents.at(0);
|
||||
}
|
||||
else if (f_contents.size() >= 2) {
|
||||
f_name = f_contents.at(0);
|
||||
f_message = f_contents.at(1);
|
||||
}
|
||||
else
|
||||
goto end;
|
||||
|
||||
if (lobby_constructed) {
|
||||
w_lobby->append_chatmessage(f_name, f_message);
|
||||
}
|
||||
if (courtroom_constructed && courtroom_loaded) {
|
||||
w_courtroom->append_server_chatmessage(tr("[Global] %1").arg(f_name),
|
||||
f_message, "0");
|
||||
}
|
||||
}
|
||||
else if (header == "AO2CHECK") {
|
||||
send_ms_packet(new AOPacket("ID#AO2#" + get_version_string() + "#%"));
|
||||
send_ms_packet(new AOPacket("HI#" + get_hdid() + "#%"));
|
||||
|
||||
if (f_contents.size() < 1)
|
||||
goto end;
|
||||
|
||||
QStringList version_contents = f_contents.at(0).split(".");
|
||||
|
||||
if (version_contents.size() < 3)
|
||||
goto end;
|
||||
|
||||
int f_release = version_contents.at(0).toInt();
|
||||
int f_major = version_contents.at(1).toInt();
|
||||
int f_minor = version_contents.at(2).toInt();
|
||||
|
||||
if (get_release() > f_release)
|
||||
goto end;
|
||||
else if (get_release() == f_release) {
|
||||
if (get_major_version() > f_major)
|
||||
goto end;
|
||||
else if (get_major_version() == f_major) {
|
||||
if (get_minor_version() >= f_minor)
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
call_notice(tr("Outdated version! Your version: %1\n"
|
||||
"Please go to aceattorneyonline.com to update.")
|
||||
.arg(get_version_string()));
|
||||
destruct_courtroom();
|
||||
destruct_lobby();
|
||||
}
|
||||
|
||||
end:
|
||||
|
||||
delete p_packet;
|
||||
}
|
||||
|
||||
void AOApplication::append_to_demofile(QString packet_string)
|
||||
{
|
||||
if (get_demo_logging_enabled() && !log_filename.isEmpty())
|
||||
@ -719,21 +619,6 @@ end:
|
||||
delete p_packet;
|
||||
}
|
||||
|
||||
void AOApplication::send_ms_packet(AOPacket *p_packet)
|
||||
{
|
||||
p_packet->net_encode();
|
||||
|
||||
QString f_packet = p_packet->to_string();
|
||||
|
||||
net_manager->ship_ms_packet(f_packet);
|
||||
|
||||
#ifdef DEBUG_NETWORK
|
||||
qDebug() << "S(ms):" << f_packet;
|
||||
#endif
|
||||
|
||||
delete p_packet;
|
||||
}
|
||||
|
||||
void AOApplication::send_server_packet(AOPacket *p_packet, bool encoded)
|
||||
{
|
||||
if (encoded)
|
||||
|
@ -1114,3 +1114,9 @@ QStringList AOApplication::get_mount_paths()
|
||||
{
|
||||
return configini->value("mount_paths").value<QStringList>();
|
||||
}
|
||||
|
||||
bool AOApplication::get_player_count_optout()
|
||||
{
|
||||
return configini->value("player_count_optout", "false").value<QString>()
|
||||
.startsWith("true");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user