Use new HTTP-based master server client
This commit is contained in:
		
							parent
							
								
									fbce4937ef
								
							
						
					
					
						commit
						b682d69e49
					
				@ -61,7 +61,6 @@ public:
 | 
			
		||||
  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();
 | 
			
		||||
@ -119,7 +118,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
 | 
			
		||||
@ -528,9 +527,6 @@ private:
 | 
			
		||||
  QVector<server_type> server_list;
 | 
			
		||||
  QVector<server_type> favorite_list;
 | 
			
		||||
 | 
			
		||||
private slots:
 | 
			
		||||
  void ms_connect_finished(bool connected, bool will_retry);
 | 
			
		||||
 | 
			
		||||
public slots:
 | 
			
		||||
  void server_disconnected();
 | 
			
		||||
  void loading_cancelled();
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@
 | 
			
		||||
#include <QtWidgets/QWidget>
 | 
			
		||||
 | 
			
		||||
#include <QDirIterator>
 | 
			
		||||
#include <QTextBrowser>
 | 
			
		||||
#include <QTextStream>
 | 
			
		||||
 | 
			
		||||
class Lobby;
 | 
			
		||||
@ -174,6 +175,10 @@ private:
 | 
			
		||||
  QLabel *ui_log_lbl;
 | 
			
		||||
  QCheckBox *ui_log_cb;
 | 
			
		||||
 | 
			
		||||
  QWidget *ui_privacy_tab;
 | 
			
		||||
  QVBoxLayout *ui_privacy_layout;
 | 
			
		||||
  QTextBrowser *ui_privacy_policy;
 | 
			
		||||
 | 
			
		||||
  bool needs_default_audiodev();
 | 
			
		||||
  void update_values();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -1,28 +1,23 @@
 | 
			
		||||
#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
 | 
			
		||||
 | 
			
		||||
@ -31,53 +26,37 @@ public:
 | 
			
		||||
  ~NetworkManager();
 | 
			
		||||
 | 
			
		||||
  AOApplication *ao_app;
 | 
			
		||||
  QTcpSocket *ms_socket;
 | 
			
		||||
  QNetworkAccessManager *http;
 | 
			
		||||
  QTcpSocket *server_socket;
 | 
			
		||||
  QDnsLookup *ms_dns;
 | 
			
		||||
  QTimer *ms_reconnect_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
 | 
			
		||||
  QString ms_baseurl = "https://ms3.oldmud0.workers.dev";
 | 
			
		||||
 | 
			
		||||
  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 timeout_milliseconds = 5000;
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
private slots:
 | 
			
		||||
  void heartbeat_playing();
 | 
			
		||||
  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
 | 
			
		||||
 | 
			
		||||
@ -16,8 +16,6 @@ AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv)
 | 
			
		||||
 | 
			
		||||
  net_manager = new NetworkManager(this);
 | 
			
		||||
  discord = new AttorneyOnline::Discord();
 | 
			
		||||
  QObject::connect(net_manager, SIGNAL(ms_connect_finished(bool, bool)),
 | 
			
		||||
                   SLOT(ms_connect_finished(bool, bool)));
 | 
			
		||||
  qApp->setStyleSheet("QFrame {background-color:transparent;} QAbstractItemView {background-color: transparent; color: black;}; QLineEdit {background-color:transparent;}");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -153,6 +151,7 @@ void AOApplication::loading_cancelled()
 | 
			
		||||
  w_lobby->hide_loading_overlay();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
void AOApplication::ms_connect_finished(bool connected, bool will_retry)
 | 
			
		||||
{
 | 
			
		||||
  if (connected) {
 | 
			
		||||
@ -179,6 +178,7 @@ void AOApplication::ms_connect_finished(bool connected, bool will_retry)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
void AOApplication::call_settings_menu()
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
#include "courtroom.h"
 | 
			
		||||
#include "lobby.h"
 | 
			
		||||
#include "bass.h"
 | 
			
		||||
#include "networkmanager.h"
 | 
			
		||||
 | 
			
		||||
AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
 | 
			
		||||
    : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint)
 | 
			
		||||
@ -902,7 +903,23 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app)
 | 
			
		||||
 | 
			
		||||
  ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_log_cb);
 | 
			
		||||
 | 
			
		||||
  // privacy policy.
 | 
			
		||||
  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_layout->setContentsMargins(0, 0, 0, 0);
 | 
			
		||||
 | 
			
		||||
  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!
 | 
			
		||||
  setUpdatesEnabled(true);
 | 
			
		||||
}
 | 
			
		||||
@ -976,6 +993,18 @@ void AOOptionsDialog::update_values() {
 | 
			
		||||
  ui_blips_volume_spinbox->setValue(ao_app->get_default_blip());
 | 
			
		||||
  ui_bliprate_spinbox->setValue(ao_app->read_blip_rate());
 | 
			
		||||
  ui_default_showname_textbox->setText(ao_app->get_default_showname());
 | 
			
		||||
 | 
			
		||||
  ao_app->net_manager->request_document(MSDocumentType::PrivacyPolicy, [this](QString document) {
 | 
			
		||||
    if (document.isEmpty())
 | 
			
		||||
      document = tr("Couldn't get the privacy policy.");
 | 
			
		||||
    bool isHtml = document.startsWith("<!DOCTYPE", Qt::CaseInsensitive)
 | 
			
		||||
        || document.startsWith("<html>", Qt::CaseInsensitive);
 | 
			
		||||
    if (isHtml) {
 | 
			
		||||
      ui_privacy_policy->setHtml(document);
 | 
			
		||||
    } else {
 | 
			
		||||
      ui_privacy_policy->setMarkdown(document);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void AOOptionsDialog::save_pressed()
 | 
			
		||||
 | 
			
		||||
@ -4307,8 +4307,9 @@ void Courtroom::on_ooc_return_pressed()
 | 
			
		||||
 | 
			
		||||
  if (server_ooc)
 | 
			
		||||
    ao_app->send_server_packet(f_packet);
 | 
			
		||||
  else
 | 
			
		||||
    ao_app->send_ms_packet(f_packet);
 | 
			
		||||
  else {
 | 
			
		||||
    // ao_app->send_ms_packet(f_packet);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  ui_ooc_chat_message->clear();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -101,6 +101,8 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow()
 | 
			
		||||
  ui_connect->setEnabled(false);
 | 
			
		||||
 | 
			
		||||
  list_servers();
 | 
			
		||||
  get_motd();
 | 
			
		||||
  check_for_updates();
 | 
			
		||||
 | 
			
		||||
  set_widgets();
 | 
			
		||||
}
 | 
			
		||||
@ -324,9 +326,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()
 | 
			
		||||
@ -492,9 +498,10 @@ void Lobby::on_chatfield_return_pressed()
 | 
			
		||||
  QString f_header = "CT";
 | 
			
		||||
  QStringList f_contents{ui_chatname->text(), ui_chatmessage->text()};
 | 
			
		||||
 | 
			
		||||
  AOPacket *f_packet = new AOPacket(f_header, f_contents);
 | 
			
		||||
  append_chatmessage("", "Stubbed lmao");
 | 
			
		||||
 | 
			
		||||
  ao_app->send_ms_packet(f_packet);
 | 
			
		||||
//  AOPacket *f_packet = new AOPacket(f_header, f_contents);
 | 
			
		||||
//  ao_app->send_ms_packet(f_packet);
 | 
			
		||||
 | 
			
		||||
  ui_chatmessage->clear();
 | 
			
		||||
}
 | 
			
		||||
@ -537,6 +544,34 @@ 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.");
 | 
			
		||||
    bool isHtml = document.startsWith("<!DOCTYPE", Qt::CaseInsensitive)
 | 
			
		||||
        || document.startsWith("<html>", Qt::CaseInsensitive);
 | 
			
		||||
    if (isHtml) {
 | 
			
		||||
      ui_chatbox->setHtml(document);
 | 
			
		||||
    } else {
 | 
			
		||||
      ui_chatbox->setMarkdown(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(
 | 
			
		||||
 | 
			
		||||
@ -55,7 +55,7 @@ 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.w_lobby->show();
 | 
			
		||||
  return main_app.exec();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,53 +4,121 @@
 | 
			
		||||
#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);
 | 
			
		||||
 | 
			
		||||
  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<QString>();
 | 
			
		||||
  if (master_config != "")
 | 
			
		||||
    ms_nosrv_hostname = master_config;
 | 
			
		||||
  if (!master_config.isEmpty())
 | 
			
		||||
    ms_baseurl = master_config;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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());
 | 
			
		||||
  req.setTransferTimeout(timeout_milliseconds);
 | 
			
		||||
 | 
			
		||||
#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)
 | 
			
		||||
{
 | 
			
		||||
  QObject::connect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), this,
 | 
			
		||||
                   SLOT(on_ms_socket_error(QAbstractSocket::SocketError)));
 | 
			
		||||
  QJsonDocument json = QJsonDocument::fromJson(reply->readAll());
 | 
			
		||||
  if (json.isNull()) {
 | 
			
		||||
    qCritical().noquote() << "Invalid JSON response from" << reply->url();
 | 
			
		||||
    reply->deleteLater();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  QObject::connect(ms_socket, SIGNAL(connected()), this,
 | 
			
		||||
                   SLOT(on_ms_nosrv_connect_success()));
 | 
			
		||||
  ms_socket->connectToHost(ms_nosrv_hostname, ms_port);
 | 
			
		||||
  qDebug().noquote() << "Got valid response from" << reply->url();
 | 
			
		||||
 | 
			
		||||
  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::heartbeat_playing()
 | 
			
		||||
{
 | 
			
		||||
  // 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::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());
 | 
			
		||||
  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::connect_to_server(server_type p_server)
 | 
			
		||||
@ -61,154 +129,11 @@ void NetworkManager::connect_to_server(server_type p_server)
 | 
			
		||||
  server_socket->connectToHost(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());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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, SIGNAL(finished()), this, SLOT(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();
 | 
			
		||||
          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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
{
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  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();
 | 
			
		||||
@ -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);
 | 
			
		||||
 | 
			
		||||
@ -67,8 +67,8 @@ void AOApplication::ms_packet_received(AOPacket *p_packet)
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  else if (header == "AO2CHECK") {
 | 
			
		||||
    send_ms_packet(new AOPacket("ID#AO2#" + get_version_string() + "#%"));
 | 
			
		||||
    send_ms_packet(new AOPacket("HI#" + get_hdid() + "#%"));
 | 
			
		||||
//    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;
 | 
			
		||||
@ -718,21 +718,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)
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user