diff --git a/.github/workflows/build-all-merge-master.yml b/.github/workflows/build-all-merge-master.yml index 299335755..618e14b32 100644 --- a/.github/workflows/build-all-merge-master.yml +++ b/.github/workflows/build-all-merge-master.yml @@ -41,6 +41,13 @@ jobs: mkdir "3rd/bass" mkdir "3rd/discord-rpc" + - name: Fetch OpenSSL (user) + shell: bash + working-directory: ${{env.parentworkspace}} + run: | + curl -L https://cdn.discordapp.com/attachments/457180869229019159/962750500632150168/openssl-1.1.1n.zip -o openssl.zip + unzip openssl.zip + - name: Fetch Discord external library shell: bash working-directory: ${{env.parentworkspace}} @@ -148,6 +155,8 @@ jobs: shell: bash run: | windeployqt.exe --quick --compiler-runtime . + cp ../libssl-1_1.dll . + cp ../libcrypto-1_1.dll . cp ../discord-rpc/win32-dynamic/bin/discord-rpc.dll . cp ../bass.dll . cp ../bassopus.dll . diff --git a/.github/workflows/build-all.yml b/.github/workflows/build-all.yml index 412866bfe..410109847 100644 --- a/.github/workflows/build-all.yml +++ b/.github/workflows/build-all.yml @@ -28,6 +28,13 @@ jobs: mkdir "3rd/bass" mkdir "3rd/discord-rpc" + - name: Fetch OpenSSL (user) + shell: bash + working-directory: ${{env.parentworkspace}} + run: | + curl -L https://cdn.discordapp.com/attachments/457180869229019159/962750500632150168/openssl-1.1.1n.zip -o openssl.zip + unzip openssl.zip + - name: Fetch Discord external library shell: bash working-directory: ${{env.parentworkspace}} @@ -135,6 +142,8 @@ jobs: shell: bash run: | windeployqt.exe --quick --compiler-runtime . + cp ../libssl-1_1.dll . + cp ../libcrypto-1_1.dll . cp ../discord-rpc/win32-dynamic/bin/discord-rpc.dll . cp ../bass.dll . cp ../bassopus.dll . diff --git a/dronline-client.pro b/dronline-client.pro index 185e8f27f..7449ff0be 100644 --- a/dronline-client.pro +++ b/dronline-client.pro @@ -49,6 +49,7 @@ HEADERS += \ src/drchatlog.h \ src/drdiscord.h \ src/dreffectmovie.h \ + src/drmasterclient.h \ src/drmovie.h \ src/drpacket.h \ src/drpather.h \ @@ -110,6 +111,7 @@ SOURCES += \ src/drcharactermovie.cpp \ src/drchatlog.cpp \ src/dreffectmovie.cpp \ + src/drmasterclient.cpp \ src/drmovie.cpp \ src/drpacket.cpp \ src/drpather.cpp \ @@ -127,7 +129,6 @@ SOURCES += \ src/lobby.cpp \ src/logger.cpp \ src/main.cpp \ - src/master_socket.cpp \ src/misc_functions.cpp \ src/path_functions.cpp \ src/server_socket.cpp \ diff --git a/res/ui/config_panel.ui b/res/ui/config_panel.ui index cd69c381c..6f812fe3a 100644 --- a/res/ui/config_panel.ui +++ b/res/ui/config_panel.ui @@ -117,6 +117,29 @@ + + + + Server Advertiser + + + + + + Address: + + + + + + + https://serveraddress.addr/ + + + + + + diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp index 8496f26bc..d51157c09 100644 --- a/src/aoapplication.cpp +++ b/src/aoapplication.cpp @@ -5,6 +5,7 @@ #include "courtroom.h" #include "debug_functions.h" #include "drdiscord.h" +#include "drmasterclient.h" #include "drpacket.h" #include "drserversocket.h" #include "lobby.h" @@ -15,17 +16,11 @@ #include #include -const QString AOApplication::MASTER_NAME = "Master"; -const QString AOApplication::MASTER_HOST = "master.aceattorneyonline.com"; -const int AOApplication::MASTER_PORT = 27016; -const int AOApplication::MASTER_RECONNECT_DELAY = 5000; - AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv) { ao_config = new AOConfig(this); ao_config_panel = new AOConfigPanel(this); dr_discord = new DRDiscord(this); - m_master_socket = new DRServerSocket(this); m_server_socket = new DRServerSocket(this); connect(ao_config, SIGNAL(theme_changed(QString)), this, SLOT(handle_theme_modification())); @@ -47,23 +42,15 @@ AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv) connect(ao_config, SIGNAL(discord_hide_server_changed(bool)), dr_discord, SLOT(set_hide_server(bool))); connect(ao_config, SIGNAL(discord_hide_character_changed(bool)), dr_discord, SLOT(set_hide_character(bool))); - connect(m_master_socket, SIGNAL(connected_to_server()), this, SLOT(_p_send_master_handshake())); - connect(m_master_socket, SIGNAL(socket_error(QString)), this, SLOT(_p_handle_master_error(QString))); - connect(m_master_socket, SIGNAL(packet_received(DRPacket)), this, SLOT(_p_handle_master_packet(DRPacket))); - connect(m_server_socket, SIGNAL(disconnected_from_server()), this, SLOT(_p_handle_server_disconnection())); connect(m_server_socket, SIGNAL(packet_received(DRPacket)), this, SLOT(_p_handle_server_packet(DRPacket))); - -#ifndef QT_DEBUG - connect_to_master(); -#endif } AOApplication::~AOApplication() { + qInfo() << "Closing Danganronpa Online..."; destruct_lobby(); destruct_courtroom(); - qInfo() << "Closing Danganronpa Online..."; } int AOApplication::get_client_id() const @@ -154,9 +141,14 @@ DRDiscord *AOApplication::get_discord() const return dr_discord; } -bool AOApplication::is_server_compatible() const +VersionNumber AOApplication::get_server_client_version() const { - return feature_version_compatible; + return m_server_client_version; +} + +VersionStatus AOApplication::get_server_client_version_status() const +{ + return m_server_client_version_status; } void AOApplication::handle_theme_modification() @@ -175,16 +167,6 @@ void AOApplication::handle_audiotracks_reloading() emit reload_audiotracks(); } -void AOApplication::set_favorite_list() -{ - m_favorite_server_list = read_serverlist_txt(); -} - -QVector &AOApplication::get_favorite_list() -{ - return m_favorite_server_list; -} - QString AOApplication::get_current_char() { if (!is_courtroom_constructed) @@ -228,25 +210,6 @@ bool AOApplication::get_first_person_enabled() return ao_config->get_bool("first_person", false); } -void AOApplication::add_favorite_server(int p_server) -{ - if (p_server < 0 || p_server >= m_server_list.size()) - return; - - server_type fav_server = m_server_list.at(p_server); - - QString str_port = QString::number(fav_server.port); - - QString server_line = fav_server.ip + ":" + str_port + ":" + fav_server.name; - - write_to_serverlist_txt(server_line); -} - -QVector &AOApplication::get_server_list() -{ - return m_server_list; -} - void AOApplication::load_fonts() { QFontDatabase l_database; diff --git a/src/aoapplication.h b/src/aoapplication.h index ffc7827d6..8d0ee6d22 100644 --- a/src/aoapplication.h +++ b/src/aoapplication.h @@ -8,6 +8,7 @@ class AOConfig; class AOConfigPanel; class Courtroom; class DRDiscord; +class DRMasterClient; class DRServerSocket; class Lobby; @@ -21,21 +22,13 @@ class AOApplication : public QApplication Q_OBJECT public: - static const QString MASTER_NAME; - static const QString MASTER_HOST; - static const int MASTER_PORT; - static const int MASTER_RECONNECT_DELAY; - AOApplication(int &argc, char **argv); ~AOApplication(); int get_client_id() const; void set_client_id(int id); - void connect_to_master(); - void send_master_packet(DRPacket packet); - void request_server_list(); - void connect_to_server(server_type server); + void connect_to_server(DRServerInfo server); void send_server_packet(DRPacket packet); Lobby *get_lobby() const; @@ -48,21 +41,17 @@ class AOApplication : public QApplication DRDiscord *get_discord() const; - bool is_server_compatible() const; + VersionNumber get_server_client_version() const; + VersionStatus get_server_client_version_status() const; /////////////////////////////////////////// - void set_favorite_list(); - QVector &get_favorite_list(); - void add_favorite_server(int p_server); - - QVector &get_server_list(); - // Returns the character the player has currently selected QString get_current_char(); // implementation in path_functions.cpp QString get_base_path(); + QString get_base_file_path(QString file); QString get_character_folder_path(QString character); QString get_character_path(QString p_character, QString p_file); // QString get_demothings_path(); @@ -102,18 +91,12 @@ class AOApplication : public QApplication // TODO document what this does QStringList get_sfx_list(); - // Appends the argument string to serverlist.txt - void write_to_serverlist_txt(QString p_line); - // Writes to note file void write_note(QString p_text, QString filename); // appends to note file void append_note(QString p_line, QString filename); - // Returns the contents of serverlist.txt - QVector read_serverlist_txt(); - QString read_ini(QString p_identifier, QString p_path); QString read_theme_ini(QString p_identifier, QString p_file); @@ -163,18 +146,12 @@ class AOApplication : public QApplication // Returns effect on cc_config according to index QStringList get_effect(int index); - // Returns wtce on cc_config according to index - QStringList get_wtce(int index); - // Returns the side of the p_char character from that characters ini file QString get_char_side(QString p_char); // Returns the showname from the ini of p_char QString get_showname(QString p_char); - // Returns showname from showname.ini - QString read_showname(QString p_char); - // Returns the value of chat from the specific p_char's ini file QString get_chat(QString p_char); @@ -212,7 +189,6 @@ public slots: AOConfigPanel *ao_config_panel = nullptr; DRDiscord *dr_discord = nullptr; - DRServerSocket *m_master_socket = nullptr; DRServerSocket *m_server_socket = nullptr; Lobby *m_lobby = nullptr; @@ -223,7 +199,9 @@ public slots: bool is_courtroom_loaded = false; ///////////////server metadata//////////////// - bool feature_version_compatible = false; + + VersionNumber m_server_client_version; + VersionStatus m_server_client_version_status = VersionStatus::Ok; ///////////////loading info/////////////////// // player number, it's hardly used but might be needed for some old servers @@ -238,13 +216,7 @@ public slots: int m_music_count = 0; int m_loaded_music = 0; - QVector m_server_list; - QVector m_favorite_server_list; - private slots: - void _p_send_master_handshake(); - void _p_handle_master_error(QString); - void _p_handle_master_packet(DRPacket); void _p_handle_server_disconnection(); void _p_handle_server_packet(DRPacket); void on_courtroom_closing(); diff --git a/src/aobutton.cpp b/src/aobutton.cpp index fb1b443dd..137775918 100644 --- a/src/aobutton.cpp +++ b/src/aobutton.cpp @@ -34,10 +34,17 @@ void AOButton::set_image(QString p_image) l_hover_image = m_image; } - this->setStyleSheet("QPushButton {border-image:url(\"" + m_image + - "\");}" - "QPushButton:hover {border-image:url(\"" + - l_hover_image + "\");}"); + setStyleSheet("QPushButton {border-image:url(\"" + m_image + + "\");}" + "QPushButton:hover {border-image:url(\"" + + l_hover_image + "\");}"); + setText(m_image.isEmpty() ? m_text : nullptr); +} + +void AOButton::set_image_and_text(QString p_image, QString p_text) +{ + m_text = p_text; + set_image(p_image); } void AOButton::refresh_image() diff --git a/src/aobutton.h b/src/aobutton.h index ba4565c4d..6b6443512 100644 --- a/src/aobutton.h +++ b/src/aobutton.h @@ -15,12 +15,14 @@ class AOButton : public QPushButton QString get_image(); bool has_image(); void set_image(QString p_image); + void set_image_and_text(QString p_image, QString p_text); void refresh_image(); private: AOApplication *ao_app = nullptr; QString m_image; QString m_image_stem; + QString m_text; }; #endif // AOBUTTON_H diff --git a/src/aoconfig.cpp b/src/aoconfig.cpp index 83e84223b..823ff7b7e 100644 --- a/src/aoconfig.cpp +++ b/src/aoconfig.cpp @@ -50,6 +50,7 @@ private slots: QStringList notification_filter; QString username; QString callwords; + QString server_advertiser; bool server_alerts; bool discord_presence = false; bool discord_hide_server = false; @@ -137,6 +138,7 @@ void AOConfigPrivate::read_file() username = cfg.value("username").toString(); showname = cfg.value("showname").toString(); callwords = cfg.value("callwords").toString(); + server_advertiser = cfg.value("server_advertiser", "https://servers.aceattorneyonline.com").toString(); server_alerts = cfg.value("server_alerts", true).toBool(); discord_presence = cfg.value("discord_presence", true).toBool(); @@ -228,6 +230,7 @@ void AOConfigPrivate::save_file() cfg.setValue("username", username); cfg.setValue("showname", showname); cfg.setValue("callwords", callwords); + cfg.setValue("server_advertiser", server_advertiser); cfg.setValue("server_alerts", server_alerts); cfg.setValue("discord_presence", discord_presence); @@ -385,6 +388,11 @@ QString AOConfig::callwords() const return d->callwords; } +QString AOConfig::server_advertiser() const +{ + return d->server_advertiser; +} + bool AOConfig::server_alerts_enabled() const { return d->server_alerts; @@ -688,6 +696,14 @@ void AOConfig::set_callwords(QString p_string) d->invoke_signal("callwords_changed", Q_ARG(QString, p_string)); } +void AOConfig::set_server_advertiser(QString p_address) +{ + if (d->server_advertiser == p_address) + return; + d->server_advertiser = p_address; + d->invoke_signal("server_advertiser_changed", Q_ARG(QString, p_address)); +} + void AOConfig::set_server_alerts(bool p_enabled) { if (d->server_alerts == p_enabled) diff --git a/src/aoconfig.h b/src/aoconfig.h index ca05a1df2..841f94d90 100644 --- a/src/aoconfig.h +++ b/src/aoconfig.h @@ -28,6 +28,7 @@ class AOConfig : public QObject QString showname_placeholder() const; QString character_ini(QString base_character) const; QString callwords() const; + QString server_advertiser() const; bool server_alerts_enabled() const; bool discord_presence() const; bool discord_hide_server() const; @@ -84,6 +85,7 @@ public slots: void clear_showname_placeholder(); void set_character_ini(QString base_character, QString target_character); void set_callwords(QString p_string); + void set_server_advertiser(QString address); void set_server_alerts(bool p_enabled); void set_discord_presence(const bool p_enabled); void set_discord_hide_server(const bool p_enabled); @@ -133,6 +135,7 @@ public slots: // general void username_changed(QString); void callwords_changed(QString); + void server_advertiser_changed(QString); void server_alerts_changed(bool); void discord_presence_changed(bool); void discord_hide_server_changed(bool); diff --git a/src/aoconfigpanel.cpp b/src/aoconfigpanel.cpp index 84065d510..7cd998bc4 100644 --- a/src/aoconfigpanel.cpp +++ b/src/aoconfigpanel.cpp @@ -47,6 +47,7 @@ AOConfigPanel::AOConfigPanel(AOApplication *p_ao_app, QWidget *p_parent) // general ui_username = AO_GUI_WIDGET(QLineEdit, "username"); ui_callwords = AO_GUI_WIDGET(QLineEdit, "callwords"); + ui_advertiser = AO_GUI_WIDGET(QLineEdit, "advertiser"); ui_server_alerts = AO_GUI_WIDGET(QCheckBox, "server_alerts"); ui_discord_presence = AO_GUI_WIDGET(QGroupBox, "discord_presence"); ui_discord_hide_server = AO_GUI_WIDGET(QCheckBox, "discord_hide_server"); @@ -122,6 +123,7 @@ AOConfigPanel::AOConfigPanel(AOApplication *p_ao_app, QWidget *p_parent) // general connect(m_config, SIGNAL(username_changed(QString)), ui_username, SLOT(setText(QString))); connect(m_config, SIGNAL(callwords_changed(QString)), ui_callwords, SLOT(setText(QString))); + connect(m_config, SIGNAL(server_advertiser_changed(QString)), ui_advertiser, SLOT(setText(QString))); connect(m_config, SIGNAL(server_alerts_changed(bool)), ui_server_alerts, SLOT(setChecked(bool))); connect(m_config, SIGNAL(discord_presence_changed(bool)), ui_discord_presence, SLOT(setChecked(bool))); connect(m_config, SIGNAL(discord_hide_server_changed(bool)), ui_discord_hide_server, SLOT(setChecked(bool))); @@ -192,6 +194,7 @@ AOConfigPanel::AOConfigPanel(AOApplication *p_ao_app, QWidget *p_parent) // general connect(ui_username, SIGNAL(editingFinished()), this, SLOT(username_editing_finished())); connect(ui_callwords, SIGNAL(editingFinished()), this, SLOT(callwords_editing_finished())); + connect(ui_advertiser, SIGNAL(editingFinished()), this, SLOT(advertiser_editing_finished())); connect(ui_server_alerts, SIGNAL(toggled(bool)), m_config, SLOT(set_server_alerts(bool))); connect(ui_discord_presence, SIGNAL(toggled(bool)), m_config, SLOT(set_discord_presence(bool))); connect(ui_discord_hide_server, SIGNAL(toggled(bool)), m_config, SLOT(set_discord_hide_server(const bool))); @@ -254,6 +257,7 @@ AOConfigPanel::AOConfigPanel(AOApplication *p_ao_app, QWidget *p_parent) // general ui_username->setText(m_config->username()); ui_callwords->setText(m_config->callwords()); + ui_advertiser->setText(m_config->server_advertiser()); ui_server_alerts->setChecked(m_config->server_alerts_enabled()); // game @@ -594,6 +598,11 @@ void AOConfigPanel::showname_editing_finished() m_config->set_showname(ui_showname->text()); } +void AOConfigPanel::advertiser_editing_finished() +{ + m_config->set_server_advertiser(ui_advertiser->text()); +} + void AOConfigPanel::callwords_editing_finished() { m_config->set_callwords(ui_callwords->text()); diff --git a/src/aoconfigpanel.h b/src/aoconfigpanel.h index a193530e7..8598bdcfd 100644 --- a/src/aoconfigpanel.h +++ b/src/aoconfigpanel.h @@ -86,6 +86,7 @@ private slots: // general QLineEdit *ui_username = nullptr; QLineEdit *ui_callwords = nullptr; + QLineEdit *ui_advertiser = nullptr; QCheckBox *ui_server_alerts = nullptr; QGroupBox *ui_discord_presence = nullptr; QCheckBox *ui_discord_hide_server = nullptr; @@ -155,6 +156,7 @@ private slots: private slots: void username_editing_finished(); void showname_editing_finished(); + void advertiser_editing_finished(); void callwords_editing_finished(); }; diff --git a/src/commondefs.cpp b/src/commondefs.cpp index 16b2ca463..73bc6b3a6 100644 --- a/src/commondefs.cpp +++ b/src/commondefs.cpp @@ -5,6 +5,9 @@ const QString BACKGROUND_DEFAULT_NAME = "gs4"; const QString BASE_CONFIG_INI = "/base/config.ini"; +const QString BASE_SERVER_BROWSER_INI = "server_browser.ini"; +const QString BASE_FAVORITE_SERVERS_INI = "favorite_servers.ini"; +const QString BASE_SERVERLIST_TXT = "serverlist.txt"; const QString CHARACTER_CHAR_INI = "char.ini"; const QString CHARACTER_SOUNDS_INI = "sounds.ini"; diff --git a/src/commondefs.h b/src/commondefs.h index 6c2f60780..e3cea60d8 100644 --- a/src/commondefs.h +++ b/src/commondefs.h @@ -5,6 +5,9 @@ class QString; extern const QString BACKGROUND_DEFAULT_NAME; extern const QString BASE_CONFIG_INI; +extern const QString BASE_SERVER_BROWSER_INI; +extern const QString BASE_FAVORITE_SERVERS_INI; +extern const QString BASE_SERVERLIST_TXT; extern const QString CHARACTER_CHAR_INI; extern const QString CHARACTER_SOUNDS_INI; diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 62e4c02e4..9bb79acd0 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -636,7 +636,7 @@ void Courtroom::on_ic_message_return_pressed() f_text_color = QString::number(m_text_color); packet_contents.append(f_text_color); - if (ao_app->is_server_compatible()) + if (ao_app->get_server_client_version_status() == VersionStatus::Ok) { // showname packet_contents.append(ao_config->showname()); @@ -2126,7 +2126,6 @@ void Courtroom::on_back_to_lobby_clicked() hide(); ao_app->construct_lobby(); - ao_app->get_lobby()->list_servers(); ao_app->get_lobby()->set_choose_a_server(); ao_app->destruct_courtroom(); } diff --git a/src/datatypes.cpp b/src/datatypes.cpp index 9e87853ea..42f638689 100644 --- a/src/datatypes.cpp +++ b/src/datatypes.cpp @@ -17,3 +17,61 @@ QMap DR::get_default_color_map() return default_color_map; } + +QString DRServerInfo::to_info() const +{ + QString r_info = "Unnamed Server"; + + if (!name.isEmpty()) + { + r_info = name; + } + else if (!address.isEmpty()) + { + r_info = to_address(); + } + + return r_info; +} + +QString DRServerInfo::to_address() const +{ + return address + ":" + QString::number(port); +} + +bool DRServerInfo::operator==(const DRServerInfo &o) const +{ + return address == o.address && port == o.port; +} + +bool DRServerInfo::operator!=(const DRServerInfo &other) const +{ + return !operator==(other); +} + +VersionNumber::VersionNumber() +{} + +VersionNumber::VersionNumber(int p_release, int p_major, int p_minor) + : release(p_release), major(p_major), minor(p_minor) +{} + +QString VersionNumber::to_string() const +{ + return QString("%1.%2.%3").arg(release).arg(major).arg(minor); +} + +bool VersionNumber::operator==(const VersionNumber &other) const +{ + return release == other.release && major == other.major; +} + +bool VersionNumber::operator<(const VersionNumber &other) const +{ + return release < other.release || (release == other.release && major < other.major); +} + +bool VersionNumber::operator>(const VersionNumber &other) const +{ + return release > other.release || (release == other.release && major > other.major); +} diff --git a/src/datatypes.h b/src/datatypes.h index 285dcb6e6..4a9d9c675 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -3,6 +3,7 @@ #include #include +#include class DREmote { @@ -82,30 +83,46 @@ struct DRSfx bool is_found; }; -struct server_type +class DRServerInfo { +public: QString name; - QString desc; - QString ip; + QString description; + QString address; int port; - bool is_favorite = false; + bool favorite = false; - QString to_info() const - { - QString r_info; - - if (!name.isEmpty()) - { - r_info = name; - } - else if (!ip.isEmpty()) - { - r_info = to_address(); - } - - return r_info; - } - QString to_address() const { return ip + ":" + QString::number(port); } + QString to_info() const; + QString to_address() const; + + bool operator==(const DRServerInfo &other) const; + bool operator!=(const DRServerInfo &other) const; +}; +using DRServerInfoList = QVector; + +class VersionNumber +{ +public: + int release = 0; + int major = 0; + int minor = 0; + + VersionNumber(); + VersionNumber(int release, int major, int minor); + + QString to_string() const; + + bool operator==(const VersionNumber &other) const; + bool operator>(const VersionNumber &other) const; + bool operator<(const VersionNumber &other) const; +}; + +enum class VersionStatus +{ + Ok, + NotCompatible, + ClientOutdated, + ServerOutdated, }; struct char_type diff --git a/src/drchatlog.cpp b/src/drchatlog.cpp index 61cb71b85..fe46afef0 100644 --- a/src/drchatlog.cpp +++ b/src/drchatlog.cpp @@ -15,12 +15,17 @@ DRChatLog::DRChatLog(QWidget *parent) : QTextBrowser(parent), dr_config(new AOCo void DRChatLog::append_chatmessage(QString p_name, QString p_text) { - queue_message(p_name.trimmed().isEmpty() ? "Anonymous" : p_name, p_text); + queue_message(p_name.trimmed().isEmpty() ? "Anonymous" : p_name, p_text, false); } -void DRChatLog::append_error(QString p_text) +void DRChatLog::append_information(QString p_text) { - queue_message(nullptr, p_text); + queue_message(nullptr, p_text, false); +} + +void DRChatLog::append_html(QString p_text) +{ + queue_message(nullptr, p_text, true); } void DRChatLog::reset_message_format() @@ -28,12 +33,13 @@ void DRChatLog::reset_message_format() _p_reset_log(); } -void DRChatLog::queue_message(QString p_name, QString p_text) +void DRChatLog::queue_message(QString p_name, QString p_text, bool p_is_html) { Message l_message; l_message.timestamp = QDateTime::currentDateTime(); l_message.name = p_name; l_message.text = p_text; + l_message.is_html = p_is_html; m_message_queue.append(std::move(l_message)); Q_EMIT message_queued(); } @@ -73,53 +79,58 @@ void DRChatLog::_p_write_message_queue() l_cursor.insertText(": ", l_normal_format); } - const QString l_text = l_message.text; - - class TextPiece + if (l_message.is_html) { - public: - QString text; - bool is_href = false; - - TextPiece() - {} - TextPiece(QString p_text, bool p_is_href = false) : text(p_text), is_href(p_is_href) - {} - }; - QVector l_piece_list; - - const QRegularExpression l_regex("(https?://[^\\s/$.?#].[^\\s]*)"); - QRegularExpressionMatchIterator l_iterator = l_regex.globalMatch(l_text); - { // capture all text pieces - int l_index = 0; - - while (l_iterator.hasNext()) - { - QRegularExpressionMatch l_match = l_iterator.next(); - - const int l_captureIndex = l_match.capturedStart(); - if (l_index < l_captureIndex) - l_piece_list.append(l_text.mid(l_index, l_captureIndex - l_index)); - l_piece_list.append(TextPiece(l_match.captured(), true)); - l_index = l_match.capturedEnd(); - } - - l_piece_list.append(l_text.mid(l_index)); + insertHtml(l_message.text); } - - for (const TextPiece &i_piece : qAsConst(l_piece_list)) + else { - if (i_piece.text.isEmpty()) - continue; + const QString l_text = l_message.text; - QTextCharFormat l_piece_format = l_normal_format; - if (i_piece.is_href) + class TextPiece { - l_piece_format = l_href_format; - l_piece_format.setAnchorHref(i_piece.text); + public: + QString text; + bool is_href = false; + + TextPiece() {} + TextPiece(QString p_text, bool p_is_href = false) : text(p_text), is_href(p_is_href) {} + }; + QVector l_piece_list; + + const QRegularExpression l_regex("(https?://[^\\s/$.?#].[^\\s]*)"); + QRegularExpressionMatchIterator l_iterator = l_regex.globalMatch(l_text); + { // capture all text pieces + int l_index = 0; + + while (l_iterator.hasNext()) + { + QRegularExpressionMatch l_match = l_iterator.next(); + + const int l_captureIndex = l_match.capturedStart(); + if (l_index < l_captureIndex) + l_piece_list.append(l_text.mid(l_index, l_captureIndex - l_index)); + l_piece_list.append(TextPiece(l_match.captured(), true)); + l_index = l_match.capturedEnd(); + } + + l_piece_list.append(l_text.mid(l_index)); } - l_cursor.insertText(i_piece.is_href ? QUrl(i_piece.text).toString() : i_piece.text, l_piece_format); + for (const TextPiece &i_piece : qAsConst(l_piece_list)) + { + if (i_piece.text.isEmpty()) + continue; + + QTextCharFormat l_piece_format = l_normal_format; + if (i_piece.is_href) + { + l_piece_format = l_href_format; + l_piece_format.setAnchorHref(i_piece.text); + } + + l_cursor.insertText(i_piece.is_href ? QUrl(i_piece.text).toString() : i_piece.text, l_piece_format); + } } } diff --git a/src/drchatlog.h b/src/drchatlog.h index 41758cdb7..82ce535f5 100644 --- a/src/drchatlog.h +++ b/src/drchatlog.h @@ -14,8 +14,9 @@ class DRChatLog : public QTextBrowser public: DRChatLog(QWidget *parent = nullptr); - void append_chatmessage(QString name, QString text); - void append_error(QString text); + void append_chatmessage(QString name, QString message); + void append_information(QString message); + void append_html(QString html); void reset_message_format(); signals: @@ -30,11 +31,12 @@ class DRChatLog : public QTextBrowser QDateTime timestamp; QString name; QString text; + bool is_html = false; }; QList m_message_list; QQueue m_message_queue; - void queue_message(QString name, QString text); + void queue_message(QString name, QString text, bool is_html); private slots: void _p_write_message_queue(); diff --git a/src/drmasterclient.cpp b/src/drmasterclient.cpp new file mode 100644 index 000000000..06bf590d9 --- /dev/null +++ b/src/drmasterclient.cpp @@ -0,0 +1,106 @@ +#include "drmasterclient.h" + +// qt +#include +#include +#include +#include +#include +#include + +DRMasterClient::DRMasterClient(QObject *parent) : QObject(parent), m_network(new QNetworkAccessManager(this)) +{} + +DRMasterClient::~DRMasterClient() +{} + +QString DRMasterClient::address() const +{ + return m_address; +} + +void DRMasterClient::set_address(QString p_address) +{ + if (m_address == p_address) + return; + m_address = p_address; + emit address_changed(); +} + +QString DRMasterClient::motd() const +{ + return m_motd; +} + +void DRMasterClient::request_motd() +{ + send_get_request("/motd", &DRMasterClient::process_motd); +} + +DRServerInfoList DRMasterClient::server_list() const +{ + return m_server_list; +} + +void DRMasterClient::request_server_list() +{ + send_get_request("/servers", &DRMasterClient::process_server_list); +} + +void DRMasterClient::send_get_request(QString request, Delegate delegate) +{ + if (m_address.isEmpty()) + { + qWarning() << "error: address is undefined"; + return; + } + + QNetworkRequest l_request(m_address + request); + QNetworkReply *l_reply = m_network->get(l_request); + l_reply->setParent(this); + m_pending_delegates.insert(l_reply, delegate); + connect(l_reply, SIGNAL(finished()), this, SLOT(process_request())); +} + +void DRMasterClient::process_request() +{ + QNetworkReply *l_reply = dynamic_cast(sender()); + if (!l_reply) + { + qCritical() << "error: sender is not expected object" << sender(); + return; + } + + QVariant l_data = l_reply->readAll(); + const QJsonDocument l_doc = QJsonDocument::fromJson(l_data.toByteArray()); + if (!l_doc.isNull()) + l_data = l_doc.toVariant(); + + const Delegate l_delegate = m_pending_delegates.take(l_reply); + (this->*l_delegate)(l_data); + l_reply->deleteLater(); +} + +void DRMasterClient::process_motd(QVariant p_data) +{ + m_motd = p_data.toString(); + emit motd_changed(); +} + +void DRMasterClient::process_server_list(QVariant p_data) +{ + DRServerInfoList l_server_list; + const QJsonArray l_json_array = p_data.toJsonArray(); + for (const QJsonValue &i_value : l_json_array) + { + const QJsonObject i_object = i_value.toObject(); + DRServerInfo l_server; + l_server.name = i_object.value("name").toString(); + l_server.description = i_object.value("description").toString(); + l_server.address = i_object.value("ip").toString(); + l_server.port = i_object.value("port").toInt(); + l_server_list.append(std::move(l_server)); + } + m_server_list = std::move(l_server_list); + emit server_list_changed(); +} diff --git a/src/drmasterclient.h b/src/drmasterclient.h new file mode 100644 index 000000000..0a355d665 --- /dev/null +++ b/src/drmasterclient.h @@ -0,0 +1,51 @@ +#pragma once + +// std +#include + +// qt +#include +#include +#include + +class QNetworkAccessManager; + +// src +#include "datatypes.h" + +class DRMasterClient : public QObject +{ + Q_OBJECT + +public: + DRMasterClient(QObject *parent = nullptr); + ~DRMasterClient(); + + QString address() const; + QString motd() const; + DRServerInfoList server_list() const; + +public slots: + void set_address(QString address); + void request_motd(); + void request_server_list(); + +signals: + void address_changed(); + void motd_changed(); + void server_list_changed(); + +private: + QNetworkAccessManager *m_network = nullptr; + QString m_address; + QString m_motd; + DRServerInfoList m_server_list; + using Delegate = void (DRMasterClient::*)(QVariant); + QHash m_pending_delegates; + +private slots: + void send_get_request(QString request, Delegate delegate); + void process_request(); + void process_motd(QVariant); + void process_server_list(QVariant); +}; diff --git a/src/drserversocket.cpp b/src/drserversocket.cpp index 3acf5622e..dcaac1465 100644 --- a/src/drserversocket.cpp +++ b/src/drserversocket.cpp @@ -7,7 +7,7 @@ const int DRServerSocket::RECONNECT_DELAY = 5000; namespace { -QString drFormatServerInfo(const server_type &server) +QString drFormatServerInfo(const DRServerInfo &server) { const QString l_server_info = server.to_info(); return !l_server_info.isEmpty() ? QString("<%1>").arg(l_server_info) : nullptr; @@ -35,14 +35,14 @@ bool DRServerSocket::is_connected() const return m_socket->state() == QTcpSocket::ConnectedState; } -void DRServerSocket::connect_to_server(server_type p_server, bool p_is_reconnectable) +void DRServerSocket::connect_to_server(DRServerInfo p_server, bool p_is_reconnectable) { disconnect_from_server(); m_server = p_server; is_reconnectable = p_is_reconnectable; if (is_reconnectable) m_reconnect_timer->start(); - m_socket->connectToHost(p_server.ip, p_server.port); + m_socket->connectToHost(p_server.address, p_server.port); } void DRServerSocket::disconnect_from_server() diff --git a/src/drserversocket.h b/src/drserversocket.h index b9b51bb9d..79ce73088 100644 --- a/src/drserversocket.h +++ b/src/drserversocket.h @@ -17,7 +17,7 @@ class DRServerSocket : public QObject DRServerSocket(QObject *parent = nullptr); bool is_connected() const; - void connect_to_server(server_type server, bool is_reconnectable); + void connect_to_server(DRServerInfo server, bool is_reconnectable); void disconnect_from_server(); public slots: @@ -33,7 +33,7 @@ public slots: private: static const int RECONNECT_DELAY; - server_type m_server; + DRServerInfo m_server; QTcpSocket *m_socket = nullptr; bool m_is_connected = false; QTimer *m_reconnect_timer = nullptr; diff --git a/src/lobby.cpp b/src/lobby.cpp index 9fcdf3741..c011568d0 100644 --- a/src/lobby.cpp +++ b/src/lobby.cpp @@ -5,8 +5,10 @@ #include "aoconfig.h" #include "aoimagedisplay.h" #include "commondefs.h" +#include "datatypes.h" #include "debug_functions.h" #include "drchatlog.h" +#include "drmasterclient.h" #include "drpacket.h" #include "drpather.h" #include "drtextedit.h" @@ -14,31 +16,38 @@ #include "version.h" #include +#include #include +#include +#include #include #include #include +#include #include +#include +#include Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() { ao_app = p_ao_app; ao_config = new AOConfig(this); + m_master_client = new DRMasterClient(this); this->setWindowTitle("Danganronpa Online"); ui_background = new AOImageDisplay(this, ao_app); - ui_public_servers = new AOButton(this, ao_app); - ui_favorites = new AOButton(this, ao_app); + ui_hide_public_servers = new AOButton(this, ao_app); + ui_hide_favorite_servers = new AOButton(this, ao_app); ui_refresh = new AOButton(this, ao_app); - ui_add_to_fav = new AOButton(this, ao_app); + ui_toggle_favorite = new AOButton(this, ao_app); ui_connect = new AOButton(this, ao_app); ui_version = new DRTextEdit(this); ui_version->setFrameStyle(QFrame::NoFrame); ui_version->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui_version->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui_version->setReadOnly(true); - ui_about = new AOButton(this, ao_app); + ui_config_panel = new AOButton(this, ao_app); ui_server_list = new QListWidget(this); ui_player_count = new DRTextEdit(this); ui_player_count->setFrameStyle(QFrame::NoFrame); @@ -52,9 +61,6 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() ui_chatbox = new DRChatLog(this); ui_chatbox->setOpenExternalLinks(true); ui_chatbox->setReadOnly(true); - ui_chatname = new QLineEdit(this); - ui_chatname->setPlaceholderText("Name"); - ui_chatmessage = new QLineEdit(this); ui_loading_background = new AOImageDisplay(this, ao_app); ui_loading_text = new DRTextEdit(ui_loading_background); ui_progress_bar = new QProgressBar(ui_loading_background); @@ -63,29 +69,44 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() ui_progress_bar->setStyleSheet("QProgressBar{ color: white; }"); ui_cancel = new AOButton(ui_loading_background, ao_app); - connect(ui_public_servers, SIGNAL(clicked()), this, SLOT(on_public_servers_clicked())); - connect(ui_favorites, SIGNAL(clicked()), this, SLOT(on_favorites_clicked())); + connect(ao_app, SIGNAL(reload_theme()), this, SLOT(update_widgets())); + connect(ao_config, SIGNAL(server_advertiser_changed(QString)), m_master_client, SLOT(set_address(QString))); + connect(m_master_client, SIGNAL(address_changed()), this, SLOT(request_advertiser_update())); + connect(m_master_client, SIGNAL(motd_changed()), this, SLOT(update_motd())); + connect(m_master_client, SIGNAL(server_list_changed()), this, SLOT(update_server_list())); + + connect(ui_hide_public_servers, SIGNAL(clicked()), this, SLOT(toggle_hide_public_servers())); + connect(ui_hide_favorite_servers, SIGNAL(clicked()), this, SLOT(toggle_hide_favorite_servers())); connect(ui_refresh, SIGNAL(pressed()), this, SLOT(on_refresh_pressed())); connect(ui_refresh, SIGNAL(released()), this, SLOT(on_refresh_released())); - connect(ui_add_to_fav, SIGNAL(pressed()), this, SLOT(on_add_to_fav_pressed())); - connect(ui_add_to_fav, SIGNAL(released()), this, SLOT(on_add_to_fav_released())); + connect(ui_toggle_favorite, SIGNAL(pressed()), this, SLOT(on_add_to_fav_pressed())); + connect(ui_toggle_favorite, SIGNAL(released()), this, SLOT(on_add_to_fav_released())); connect(ui_connect, SIGNAL(pressed()), this, SLOT(on_connect_pressed())); connect(ui_connect, SIGNAL(released()), this, SLOT(on_connect_released())); - connect(ui_about, SIGNAL(clicked()), this, SLOT(on_about_clicked())); - connect(ui_server_list, SIGNAL(clicked(QModelIndex)), this, SLOT(on_server_list_clicked(QModelIndex))); - connect(ui_chatmessage, SIGNAL(returnPressed()), this, SLOT(on_chatfield_return_pressed())); + connect(ui_config_panel, SIGNAL(pressed()), this, SLOT(on_config_pressed())); + connect(ui_config_panel, SIGNAL(released()), this, SLOT(on_config_released())); + connect(ui_server_list, SIGNAL(currentRowChanged(int)), this, SLOT(connect_to_server(int))); connect(ui_cancel, SIGNAL(clicked()), ao_app, SLOT(loading_cancelled())); - set_widgets(); + update_widgets(); + load_settings(); + load_favorite_server_list(); + m_master_client->set_address(ao_config->server_advertiser()); + set_choose_a_server(); +} + +Lobby::~Lobby() +{ + save_settings(); } -bool Lobby::is_public_server() const +DRServerInfoList Lobby::get_combined_server_list() { - return is_public_server_selected; + return m_combined_server_list; } // sets images, position and size -void Lobby::set_widgets() +void Lobby::update_widgets() { pos_size_type f_lobby = ao_app->get_element_dimensions("lobby", LOBBY_DESIGN_INI); @@ -116,17 +137,17 @@ void Lobby::set_widgets() set_size_and_pos(ui_background, "lobby", LOBBY_DESIGN_INI, ao_app); ui_background->set_theme_image("lobbybackground.png"); - set_size_and_pos(ui_public_servers, "public_servers", LOBBY_DESIGN_INI, ao_app); - ui_public_servers->set_image("publicservers_selected.png"); + set_size_and_pos(ui_hide_public_servers, "public_servers", LOBBY_DESIGN_INI, ao_app); + ui_hide_public_servers->set_image(m_hide_public_servers ? "publicservers_selected.png" : "publicservers.png"); - set_size_and_pos(ui_favorites, "favorites", LOBBY_DESIGN_INI, ao_app); - ui_favorites->set_image("favorites.png"); + set_size_and_pos(ui_hide_favorite_servers, "favorites", LOBBY_DESIGN_INI, ao_app); + ui_hide_favorite_servers->set_image(m_hide_favorite_servers ? "favorites_selected.png" : "favorites.png"); set_size_and_pos(ui_refresh, "refresh", LOBBY_DESIGN_INI, ao_app); ui_refresh->set_image("refresh.png"); - set_size_and_pos(ui_add_to_fav, "add_to_fav", LOBBY_DESIGN_INI, ao_app); - ui_add_to_fav->set_image("addtofav.png"); + set_size_and_pos(ui_toggle_favorite, "add_to_fav", LOBBY_DESIGN_INI, ao_app); + ui_toggle_favorite->set_image("addtofav.png"); set_size_and_pos(ui_connect, "connect", LOBBY_DESIGN_INI, ao_app); ui_connect->set_image("connect.png"); @@ -134,8 +155,14 @@ void Lobby::set_widgets() set_size_and_pos(ui_version, "version", LOBBY_DESIGN_INI, ao_app); ui_version->setText("Version: " + get_version_string()); - set_size_and_pos(ui_about, "about", LOBBY_DESIGN_INI, ao_app); - ui_about->set_image("about.png"); + set_size_and_pos(ui_config_panel, "config_panel", LOBBY_DESIGN_INI, ao_app); + ui_config_panel->set_image_and_text("lobby_config_panel.png", "Config"); + if (ui_config_panel->isHidden() || ui_config_panel->size().isEmpty()) + { + ui_config_panel->resize(64, 64); + ui_config_panel->move(0, 0); + ui_config_panel->show(); + } set_size_and_pos(ui_server_list, "server_list", LOBBY_DESIGN_INI, ao_app); ui_server_list->setStyleSheet("background-color: rgba(0, 0, 0, 0);" @@ -154,16 +181,6 @@ void Lobby::set_widgets() ui_chatbox->setReadOnly(true); ui_chatbox->setStyleSheet("QTextBrowser{background-color: rgba(0, 0, 0, 0);}"); - set_size_and_pos(ui_chatname, "chatname", LOBBY_DESIGN_INI, ao_app); - set_text_alignment(ui_chatname, "chatname", LOBBY_FONTS_INI, ao_app); - ui_chatname->setStyleSheet("background-color: rgba(0, 0, 0, 0);" - "selection-background-color: rgba(0, 0, 0, 0);"); - - set_size_and_pos(ui_chatmessage, "chatmessage", LOBBY_DESIGN_INI, ao_app); - set_text_alignment(ui_chatmessage, "chatmessage", LOBBY_FONTS_INI, ao_app); - ui_chatmessage->setStyleSheet("background-color: rgba(0, 0, 0, 0);" - "selection-background-color: rgba(0, 0, 0, 0);"); - ui_loading_background->resize(this->width(), this->height()); ui_loading_background->set_theme_image("loadingbackground.png"); @@ -184,7 +201,7 @@ void Lobby::set_widgets() set_fonts(); set_stylesheets(); - set_choose_a_server(); + update_server_listing(); } void Lobby::set_fonts() @@ -192,8 +209,6 @@ void Lobby::set_fonts() set_drtextedit_font(ui_player_count, "player_count", LOBBY_FONTS_INI, ao_app); set_font(ui_description, "description", LOBBY_FONTS_INI, ao_app); set_font(ui_chatbox, "chatbox", LOBBY_FONTS_INI, ao_app); - set_font(ui_chatname, "chatname", LOBBY_FONTS_INI, ao_app); - set_font(ui_chatmessage, "chatmessage", LOBBY_FONTS_INI, ao_app); set_drtextedit_font(ui_loading_text, "loading_text", LOBBY_FONTS_INI, ao_app); set_font(ui_server_list, "server_list", LOBBY_FONTS_INI, ao_app); } @@ -211,8 +226,6 @@ void Lobby::set_stylesheets() set_stylesheet(ui_player_count, "[PLAYER COUNT]"); set_stylesheet(ui_description, "[DESCRIPTION]"); set_stylesheet(ui_chatbox, "[CHAT BOX]"); - set_stylesheet(ui_chatname, "[CHAT NAME]"); - set_stylesheet(ui_chatmessage, "[CHAT MESSAGE]"); set_stylesheet(ui_loading_text, "[LOADING TEXT]"); set_stylesheet(ui_server_list, "[SERVER LIST]"); } @@ -234,9 +247,9 @@ void Lobby::set_loading_text(QString p_text) ui_loading_text->append(p_text); } -server_type Lobby::get_selected_server() +DRServerInfo Lobby::get_selected_server() { - return m_last_server; + return m_current_server; } void Lobby::set_loading_value(int p_value) @@ -244,155 +257,300 @@ void Lobby::set_loading_value(int p_value) ui_progress_bar->setValue(p_value); } -void Lobby::on_public_servers_clicked() +void Lobby::load_settings() { - ui_public_servers->set_image("publicservers_selected.png"); - ui_favorites->set_image("favorites.png"); - - list_servers(); + QSettings l_ini(ao_app->get_base_file_path(BASE_SERVER_BROWSER_INI), QSettings::IniFormat); + l_ini.setIniCodec("UTF-8"); - is_public_server_selected = true; + l_ini.beginGroup("filters"); + hide_public_servers(l_ini.value("hide_public", false).toBool()); + hide_favorite_servers(l_ini.value("hide_favorites", false).toBool()); + l_ini.endGroup(); } -void Lobby::on_favorites_clicked() +void Lobby::save_settings() { - ui_favorites->set_image("favorites_selected.png"); - ui_public_servers->set_image("publicservers.png"); - - ao_app->set_favorite_list(); - // ao_app->favorite_list = read_serverlist_txt(); + QSettings l_ini(ao_app->get_base_file_path(BASE_SERVER_BROWSER_INI), QSettings::IniFormat); + l_ini.setIniCodec("UTF-8"); + + l_ini.beginGroup("filters"); + l_ini.setValue("hide_public", m_hide_public_servers); + l_ini.setValue("hide_favorites", m_hide_favorite_servers); + l_ini.endGroup(); + l_ini.sync(); +} - list_favorites(); +void Lobby::load_favorite_server_list() +{ + const QString l_file_path = ao_app->find_asset_path(ao_app->get_base_file_path(BASE_FAVORITE_SERVERS_INI)); + if (l_file_path.isEmpty()) + { + load_legacy_favorite_server_list(); + return; + } - is_public_server_selected = false; + DRServerInfoList l_server_list; + QSettings l_ini(l_file_path, QSettings::IniFormat); + l_ini.setIniCodec("UTF-8"); + l_server_list.clear(); + for (const QString &i_group : l_ini.childGroups()) + { + l_ini.beginGroup(i_group); + DRServerInfo l_server; + l_server.name = l_ini.value("name").toString(); + l_server.address = l_ini.value("address").toString(); + l_server.port = l_ini.value("port").toInt(); + l_server.favorite = true; + l_server_list.append(std::move(l_server)); + l_ini.endGroup(); + } + set_favorite_server_list(l_server_list); } -void Lobby::on_refresh_pressed() +void Lobby::load_legacy_favorite_server_list() { - ui_refresh->set_image("refresh_pressed.png"); + DRServerInfoList l_server_list; + QFile l_file(ao_app->get_base_file_path(BASE_SERVERLIST_TXT)); + if (l_file.open(QIODevice::ReadOnly)) + { + QTextStream in(&l_file); + while (!in.atEnd()) + { + const QStringList l_contents = in.readLine().split(":"); + if (l_contents.length() < 3) + continue; + DRServerInfo f_server; + f_server.address = l_contents.at(0); + f_server.port = l_contents.at(1).toInt(); + f_server.name = l_contents.at(2); + f_server.favorite = true; + l_server_list.append(std::move(f_server)); + } + } + set_favorite_server_list(l_server_list); } -void Lobby::on_refresh_released() +void Lobby::save_favorite_server_list() { - ui_refresh->set_image("refresh.png"); - ao_app->request_server_list(); + QSettings l_ini(ao_app->get_base_file_path(BASE_FAVORITE_SERVERS_INI), QSettings::IniFormat); + l_ini.setIniCodec("UTF-8"); + + l_ini.clear(); + for (int i = 0; i < m_favorite_server_list.length(); ++i) + { + const DRServerInfo &i_server = m_favorite_server_list.at(i); + l_ini.beginGroup(QString::number(i)); + l_ini.setValue("name", i_server.name); + l_ini.setValue("address", i_server.address); + l_ini.setValue("port", i_server.port); + l_ini.endGroup(); + } + l_ini.sync(); } -void Lobby::on_add_to_fav_pressed() +void Lobby::request_advertiser_update() { - ui_add_to_fav->set_image("addtofav_pressed.png"); + m_master_client->request_motd(); + m_master_client->request_server_list(); } -void Lobby::on_add_to_fav_released() +void Lobby::update_motd() { - ui_add_to_fav->set_image("addtofav.png"); + ui_chatbox->append_html(m_master_client->motd()); +} - // you cant add favorites from favorites m8 - if (!is_public_server_selected) - return; +void Lobby::update_server_list() +{ + m_server_list = m_master_client->server_list(); + update_combined_server_list(); + emit server_list_changed(); +} - ao_app->add_favorite_server(ui_server_list->currentRow()); +void Lobby::set_favorite_server_list(DRServerInfoList p_server_list) +{ + m_favorite_server_list = p_server_list; + update_combined_server_list(); + emit favorite_server_list_changed(); } -void Lobby::on_connect_pressed() +void Lobby::update_combined_server_list() { - ui_connect->set_image("connect_pressed.png"); + m_combined_server_list = m_favorite_server_list + m_server_list; + update_server_listing(); } -void Lobby::on_connect_released() +void Lobby::update_server_listing() { - if (!ao_app->is_server_compatible()) + ui_server_list->clear(); + const QIcon l_favorite_icon = QPixmap(ao_app->find_theme_asset_path("favorite_server.png")); + const QBrush l_favorite_color = ao_app->get_color("favorite_server_color", LOBBY_DESIGN_INI); + for (const DRServerInfo &l_server : qAsConst(m_combined_server_list)) { - call_warning("You are connecting to an incompatible DRO server.
" - "The client may not work properly, if at all."); + QListWidgetItem *l_server_item = new QListWidgetItem; + ui_server_list->addItem(l_server_item); + l_server_item->setText(l_server.name); + if (l_server.favorite) + { + l_server_item->setIcon(l_favorite_icon); + l_server_item->setBackground(l_favorite_color); + } } - - ui_connect->set_image("connect.png"); - ao_app->send_server_packet(DRPacket("askchaa")); + filter_server_listing(); } -void Lobby::on_about_clicked() +void Lobby::filter_server_listing() { - QMessageBox::about(this, tr("About"), get_about_message()); + for (int i = 0; i < ui_server_list->count(); ++i) + { + QListWidgetItem *l_server_item = ui_server_list->item(i); + l_server_item->setHidden(m_combined_server_list.at(i).favorite ? m_hide_favorite_servers : m_hide_public_servers); + } + select_current_server(); } -void Lobby::on_server_list_clicked(QModelIndex p_model) +void Lobby::select_current_server() { - int n_server = p_model.row(); - - if (n_server < 0) - return; - - if (is_public_server_selected) + for (int i = 0; i < ui_server_list->count(); ++i) { - QVector f_server_list = ao_app->get_server_list(); - - if (n_server >= f_server_list.size()) - return; - - m_last_server = f_server_list.at(p_model.row()); - } - else - { - if (n_server >= ao_app->get_favorite_list().size()) - return; - - m_last_server = ao_app->get_favorite_list().at(p_model.row()); - m_last_server.is_favorite = true; + QListWidgetItem *l_item = ui_server_list->item(i); + if (l_item->text() == m_current_server.name) + { + ui_server_list->scrollToItem(l_item); + ui_server_list->setCurrentItem(l_item); + ui_server_list->setFocus(); + break; + } } +} - ui_player_count->setText("Connecting..."); - ui_description->setHtml("Connecting to " + m_last_server.name + "..."); +void Lobby::hide_public_servers(bool p_on) +{ + if (m_hide_public_servers == p_on) + return; + m_hide_public_servers = p_on; + ui_hide_public_servers->set_image(m_hide_public_servers ? "publicservers_selected.png" : "publicservers.png"); + filter_server_listing(); +} - ao_app->connect_to_server(m_last_server); +void Lobby::toggle_hide_public_servers() +{ + hide_public_servers(!m_hide_public_servers); } -void Lobby::on_chatfield_return_pressed() +void Lobby::hide_favorite_servers(bool p_on) { - // no you can't send empty messages - if (ui_chatname->text() == "" || ui_chatmessage->text() == "") + if (m_hide_favorite_servers == p_on) return; + m_hide_favorite_servers = p_on; + ui_hide_favorite_servers->set_image(m_hide_favorite_servers ? "favorites_selected.png" : "favorites.png"); + filter_server_listing(); +} - QString f_header = "CT"; - QStringList f_contents{ui_chatname->text(), ui_chatmessage->text()}; - - ao_app->send_master_packet(DRPacket(f_header, f_contents)); +void Lobby::toggle_hide_favorite_servers() +{ + hide_favorite_servers(!m_hide_favorite_servers); +} - ui_chatmessage->clear(); +void Lobby::on_refresh_pressed() +{ + ui_refresh->set_image("refresh_pressed.png"); } -void Lobby::list_servers() +void Lobby::on_refresh_released() { - is_public_server_selected = true; - ui_favorites->set_image("favorites.png"); - ui_public_servers->set_image("publicservers_selected.png"); + ui_refresh->set_image("refresh.png"); + m_master_client->request_server_list(); +} - ui_server_list->clear(); +void Lobby::on_add_to_fav_pressed() +{ + ui_toggle_favorite->set_image("addtofav_pressed.png"); +} - for (const server_type &i_server : ao_app->get_server_list()) +void Lobby::on_add_to_fav_released() +{ + ui_toggle_favorite->set_image("addtofav.png"); + DRServerInfoList l_new_list = m_favorite_server_list; + if (m_current_server.favorite) + { + l_new_list.removeAll(m_current_server); + } + else if (!m_favorite_server_list.contains(m_current_server)) { - ui_server_list->addItem(i_server.name); + m_current_server.favorite = true; + + const QString l_new_name = + QInputDialog::getText(this, windowTitle(), "Name", QLineEdit::Normal, m_current_server.name); + if (!l_new_name.isEmpty()) + m_current_server.name = l_new_name; + + l_new_list.append(m_current_server); } + set_favorite_server_list(l_new_list); + save_favorite_server_list(); } -void Lobby::list_favorites() +void Lobby::on_connect_pressed() { - ui_server_list->clear(); + ui_connect->set_image("connect_pressed.png"); +} - for (const server_type &i_server : ao_app->get_favorite_list()) +void Lobby::on_connect_released() +{ + const VersionStatus l_status = ao_app->get_server_client_version_status(); + if (l_status != VersionStatus::Ok) { - ui_server_list->addItem(i_server.name); + QString l_reason; + switch (l_status) + { + case VersionStatus::NotCompatible: + l_reason = "The server did not report any client version."; + break; + case VersionStatus::ServerOutdated: + l_reason = QString("The server is outdated.
(Server version: %1, expected version: %2)") + .arg(ao_app->get_server_client_version().to_string(), get_version_number().to_string()); + break; + case VersionStatus::ClientOutdated: + l_reason = QString("Your client is outdated.
(Client version: %1, expected version: %2)") + .arg(get_version_number().to_string(), ao_app->get_server_client_version().to_string()); + break; + default: + break; + } + + call_warning("You are connecting to an incompatible DRO server.

Reason: " + l_reason + + "

" + "The client may not work properly, if at all."); } + + ui_connect->set_image("connect.png"); + ao_app->send_server_packet(DRPacket("askchaa")); } -void Lobby::append_chatmessage(QString f_name, QString f_message) +void Lobby::on_config_pressed() { - ui_chatbox->append_chatmessage(f_name, f_message); + ui_config_panel->set_image("lobby_config_panel_pressed.png"); } -void Lobby::append_error(QString f_message) +void Lobby::on_config_released() { - ui_chatbox->append_error(f_message); + ui_config_panel->set_image("lobby_config_panel.png"); + ao_app->toggle_config_panel(); +} + +void Lobby::connect_to_server(int p_row) +{ + if (p_row == -1) + return; + + const DRServerInfo l_prev_server = std::move(m_current_server); + m_current_server = m_combined_server_list.at(p_row); + if (l_prev_server != m_current_server) + { + ui_player_count->setText("Connecting..."); + ui_description->setHtml("Connecting to " + m_current_server.name + "..."); + ao_app->connect_to_server(m_current_server); + } } void Lobby::set_choose_a_server() @@ -407,7 +565,7 @@ void Lobby::set_player_count(int players_online, int max_players) ui_player_count->setText(f_string); ui_player_count->setAlignment(Qt::AlignHCenter); - QString l_text = m_last_server.desc.toHtmlEscaped(); + QString l_text = m_current_server.description.toHtmlEscaped(); const QRegExp l_regex("(https?://[^\\s/$.?#].[^\\s]*)"); if (l_text.contains(l_regex)) l_text.replace(l_regex, "\\1"); diff --git a/src/lobby.h b/src/lobby.h index 977fe72a7..fdc521654 100644 --- a/src/lobby.h +++ b/src/lobby.h @@ -8,6 +8,7 @@ class AOButton; class AOConfig; class AOImageDisplay; class DRChatLog; +class DRMasterClient; class DRTextEdit; #include @@ -24,14 +25,10 @@ class Lobby : public QMainWindow public: Lobby(AOApplication *p_ao_app); + ~Lobby(); - bool is_public_server() const; + DRServerInfoList get_combined_server_list(); - void set_widgets(); - void list_servers(); - void list_favorites(); - void append_chatmessage(QString f_name, QString f_message); - void append_error(QString f_message); void set_choose_a_server(); void set_player_count(int players_online, int max_players); void set_loading_text(QString p_text); @@ -40,39 +37,67 @@ class Lobby : public QMainWindow void set_fonts(); void show_loading_overlay(); void hide_loading_overlay(); - server_type get_selected_server(); + DRServerInfo get_selected_server(); void set_loading_value(int p_value); +signals: + void server_list_changed(); + void favorite_server_list_changed(); + void combined_server_list_changed(); + private: AOApplication *ao_app = nullptr; AOConfig *ao_config = nullptr; - server_type m_last_server; + DRMasterClient *m_master_client = nullptr; + DRServerInfoList m_server_list; + DRServerInfoList m_favorite_server_list; + DRServerInfoList m_combined_server_list; + DRServerInfo m_current_server; bool is_public_server_selected = true; // ui AOImageDisplay *ui_background = nullptr; - AOButton *ui_public_servers = nullptr; - AOButton *ui_favorites = nullptr; + AOButton *ui_hide_public_servers = nullptr; + bool m_hide_public_servers = false; + AOButton *ui_hide_favorite_servers = nullptr; + bool m_hide_favorite_servers = false; AOButton *ui_refresh = nullptr; - AOButton *ui_add_to_fav = nullptr; + AOButton *ui_toggle_favorite = nullptr; AOButton *ui_connect = nullptr; DRTextEdit *ui_version = nullptr; - AOButton *ui_about = nullptr; + AOButton *ui_config_panel = nullptr; QListWidget *ui_server_list = nullptr; DRTextEdit *ui_player_count = nullptr; QTextBrowser *ui_description = nullptr; DRChatLog *ui_chatbox = nullptr; - QLineEdit *ui_chatname = nullptr; - QLineEdit *ui_chatmessage = nullptr; AOImageDisplay *ui_loading_background = nullptr; DRTextEdit *ui_loading_text = nullptr; QProgressBar *ui_progress_bar = nullptr; AOButton *ui_cancel = nullptr; + void load_settings(); + void save_settings(); + + void load_favorite_server_list(); + void load_legacy_favorite_server_list(); + void save_favorite_server_list(); + private slots: - void on_public_servers_clicked(); - void on_favorites_clicked(); + void update_widgets(); + + void request_advertiser_update(); + void update_motd(); + void update_server_list(); + void set_favorite_server_list(DRServerInfoList server_list); + void update_combined_server_list(); + void hide_public_servers(bool on); + void toggle_hide_public_servers(); + void hide_favorite_servers(bool on); + void toggle_hide_favorite_servers(); + void update_server_listing(); + void filter_server_listing(); + void select_current_server(); void on_refresh_pressed(); void on_refresh_released(); @@ -80,9 +105,9 @@ private slots: void on_add_to_fav_released(); void on_connect_pressed(); void on_connect_released(); - void on_about_clicked(); - void on_server_list_clicked(QModelIndex p_model); - void on_chatfield_return_pressed(); + void on_config_pressed(); + void on_config_released(); + void connect_to_server(int row); }; #endif // LOBBY_H diff --git a/src/master_socket.cpp b/src/master_socket.cpp deleted file mode 100644 index 08958b1c5..000000000 --- a/src/master_socket.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "aoapplication.h" - -#include "debug_functions.h" -#include "drpacket.h" -#include "drserversocket.h" -#include "hardware_functions.h" -#include "lobby.h" -#include "version.h" - -#include - -void AOApplication::connect_to_master() -{ - server_type l_server; - l_server.name = MASTER_NAME; - l_server.ip = MASTER_HOST; - l_server.port = MASTER_PORT; - m_master_socket->connect_to_server(l_server, true); -} - -void AOApplication::send_master_packet(DRPacket p_packet) -{ - if (!m_master_socket->is_connected()) - { - qDebug() << "Failed to send packet: not connected to master"; - return; - } - - qDebug().noquote() << "M/S:" << p_packet.to_string(); - m_master_socket->send_packet(p_packet); -} - -void AOApplication::request_server_list() -{ - if (!m_master_socket->is_connected()) - { - connect_to_master(); - return; - } - m_master_socket->send_packet(DRPacket("ALL")); -} - -void AOApplication::_p_send_master_handshake() -{ - send_master_packet(DRPacket("ALL")); -} - -void AOApplication::_p_handle_master_error(QString p_error) -{ - if (!is_lobby_constructed) - return; - m_lobby->append_error(p_error); -} - -void AOApplication::_p_handle_master_packet(DRPacket p_packet) -{ - const QString l_header = p_packet.get_header(); - const QStringList l_content = p_packet.get_content(); - - if (l_header != "CHECK") - qDebug().noquote() << "M/R:" << p_packet.to_string(); - - if (l_header == "ALL") - { - m_server_list.clear(); - - for (const QString &i_string : qAsConst(l_content)) - { - server_type f_server; - QStringList sub_contents = i_string.split("&"); - - if (sub_contents.size() < 4) - { - qDebug() << "W: 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(); - - m_server_list.append(f_server); - } - - if (is_lobby_constructed) - { - m_lobby->list_servers(); - } - } - else if (l_header == "CT") - { - QString f_name, f_message; - - if (l_content.size() == 1) - { - f_name = ""; - f_message = l_content.at(0); - } - else if (l_content.size() >= 2) - { - f_name = l_content.at(0); - f_message = l_content.at(1); - } - else - return; - - if (is_lobby_constructed) - { - m_lobby->append_chatmessage(f_name, f_message); - } - } - else if (l_header == "AO2CHECK") - { - send_master_packet(DRPacket("ID", {"DRO", get_version_string()})); - send_master_packet(DRPacket("HI", {get_hdid()})); - } -} diff --git a/src/path_functions.cpp b/src/path_functions.cpp index ea7d20d1b..4fe91a029 100644 --- a/src/path_functions.cpp +++ b/src/path_functions.cpp @@ -25,6 +25,11 @@ QString AOApplication::get_base_path() return DRPather::get_application_path() + "/base/"; } +QString AOApplication::get_base_file_path(QString p_file) +{ + return get_base_path() + p_file; +} + QString AOApplication::get_character_folder_path(QString p_chr) { QString r_path = get_base_path() + "characters/" + p_chr; diff --git a/src/server_socket.cpp b/src/server_socket.cpp index 24652c2bc..ee340ab33 100644 --- a/src/server_socket.cpp +++ b/src/server_socket.cpp @@ -1,5 +1,7 @@ #include "aoapplication.h" +#include + #include "aoconfig.h" #include "courtroom.h" #include "debug_functions.h" @@ -11,9 +13,7 @@ #include "lobby.h" #include "version.h" -#include - -void AOApplication::connect_to_server(server_type p_server) +void AOApplication::connect_to_server(DRServerInfo p_server) { m_server_socket->connect_to_server(p_server, false); } @@ -55,8 +55,8 @@ void AOApplication::_p_handle_server_packet(DRPacket p_packet) if (l_content.size() == 0) return; - feature_version_compatible = false; - + m_server_client_version = VersionNumber(); + m_server_client_version_status = VersionStatus::NotCompatible; send_server_packet(DRPacket("HI", {get_hdid()})); } else if (l_header == "ID") @@ -77,9 +77,25 @@ void AOApplication::_p_handle_server_packet(DRPacket p_packet) if (is_courtroom_constructed) m_courtroom->append_server_chatmessage(l_content.at(0), l_content.at(1)); } - else if (l_header == "FL") + else if (l_header == "client_version") { - feature_version_compatible = l_content.contains("v110", Qt::CaseInsensitive); + if (l_content.size() < 3) + return; + + m_server_client_version = VersionNumber(l_content.at(0).toInt(), l_content.at(1).toInt(), l_content.at(2).toInt()); + const VersionNumber l_client_version = get_version_number(); + if (l_client_version == m_server_client_version) + { + m_server_client_version_status = VersionStatus::Ok; + } + else if (l_client_version < m_server_client_version) + { + m_server_client_version_status = VersionStatus::ClientOutdated; + } + else if (l_client_version > m_server_client_version) + { + m_server_client_version_status = VersionStatus::ServerOutdated; + } } else if (l_header == "PN") { @@ -108,11 +124,12 @@ void AOApplication::_p_handle_server_packet(DRPacket p_packet) is_courtroom_loaded = false; - server_type l_current_server = m_lobby->get_selected_server(); - if (l_current_server.is_favorite) + DRServerInfo l_current_server = m_lobby->get_selected_server(); + if (l_current_server.favorite) { const QString l_current_server_address = l_current_server.to_address(); - for (const server_type &i_server : qAsConst(m_server_list)) + const DRServerInfoList l_server_list = m_lobby->get_combined_server_list(); + for (const DRServerInfo &i_server : qAsConst(l_server_list)) { if (l_current_server_address != i_server.to_address()) continue; diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index cb249237b..3d2a3f5eb 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -60,61 +60,6 @@ void AOApplication::append_note(QString p_line, QString p_file) } } -void AOApplication::write_to_serverlist_txt(QString p_line) -{ - QFile serverlist_txt; - QString serverlist_txt_path = get_base_path() + "serverlist.txt"; - - serverlist_txt.setFileName(serverlist_txt_path); - - if (!serverlist_txt.open(QIODevice::WriteOnly | QIODevice::Append)) - { - return; - } - - QTextStream out(&serverlist_txt); - - out << "\r\n" << p_line; - - serverlist_txt.close(); -} - -QVector AOApplication::read_serverlist_txt() -{ - QVector f_server_list; - - QFile serverlist_txt; - QString serverlist_txt_path = get_base_path() + "serverlist.txt"; - - serverlist_txt.setFileName(serverlist_txt_path); - - if (!serverlist_txt.open(QIODevice::ReadOnly)) - { - return f_server_list; - } - - QTextStream in(&serverlist_txt); - - while (!in.atEnd()) - { - QString line = in.readLine(); - server_type f_server; - QStringList line_contents = line.split(":"); - - if (line_contents.size() < 3) - continue; - - f_server.ip = line_contents.at(0); - f_server.port = line_contents.at(1).toInt(); - f_server.name = line_contents.at(2); - f_server.desc = ""; - - f_server_list.append(f_server); - } - - return f_server_list; -} - /** * @brief Reads p_path and returns the value associated with key * p_identifier. If the file or key do not exist, return empty. @@ -230,7 +175,7 @@ std::optional AOApplication::maybe_color(QString p_identifier, QString p QColor AOApplication::get_color(QString p_identifier, QString p_file) { - return maybe_color(p_identifier, p_file).value_or(QColor(255, 255, 255)); + return maybe_color(p_identifier, p_file).value_or(QColor(127, 127, 127, 127)); } QString AOApplication::get_font_name(QString p_identifier, QString p_file) diff --git a/src/theme.cpp b/src/theme.cpp index 0bb3b4df3..fc5afd8a8 100644 --- a/src/theme.cpp +++ b/src/theme.cpp @@ -76,6 +76,10 @@ void set_font(QWidget *p_widget, QString p_identifier, QString ini_file, AOAppli int bold = ao_app->get_font_property(p_identifier + "_bold", ini_file); QString is_bold = (bold == 1 ? "bold" : ""); + QFont font = p_widget->font(); + font.setBold(bold); + p_widget->setFont(font); + QString style_sheet_string = class_name + " { " + "background-color: rgba(0, 0, 0, 0);\n" + "color: " + l_font_color.name(QColor::HexArgb) + ";\n" + "font: " + is_bold + ";" + " }"; p_widget->setStyleSheet(style_sheet_string); diff --git a/src/version.cpp b/src/version.cpp index 6183e8425..e6dd54ac4 100644 --- a/src/version.cpp +++ b/src/version.cpp @@ -1,11 +1,13 @@ #include "version.h" -#include - #include #include #include +#include + +#include "datatypes.h" + int get_release_version() { return 1; @@ -21,6 +23,11 @@ int get_minor_version() return 0; } +VersionNumber get_version_number() +{ + return VersionNumber(get_release_version(), get_major_version(), get_minor_version()); +} + QString get_post_version() { return "b1"; @@ -28,7 +35,7 @@ QString get_post_version() QString get_version_string() { - QString l_version = QString("%1.%2.%3").arg(get_release_version()).arg(get_major_version()).arg(get_minor_version()); + QString l_version = get_version_number().to_string(); const QString l_post = get_post_version(); if (!l_post.isEmpty()) diff --git a/src/version.h b/src/version.h index 430247c38..9e80e59b3 100644 --- a/src/version.h +++ b/src/version.h @@ -2,9 +2,12 @@ class QString; +class VersionNumber; + int get_release_version(); int get_major_version(); int get_minor_version(); +VersionNumber get_version_number(); QString get_post_version(); QString get_version_string(); QString get_about_message();