From 81988d96fe5c162f349494523c8055b9d8760847 Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Fri, 19 Apr 2019 22:02:49 +0800 Subject: [PATCH 1/5] core/telemetry_session: Only create the backend when we really need it The backend is not used until we decide to submit the testcase/telemetry, and creating it early prevents users from updating the credentials properly while the games are running. --- src/core/telemetry_session.cpp | 20 +++++++++++--------- src/core/telemetry_session.h | 1 - 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 8f9073dde..3e287f76d 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -92,13 +92,6 @@ bool VerifyLogin(const std::string& username, const std::string& token) { } TelemetrySession::TelemetrySession() { -#ifdef ENABLE_WEB_SERVICE - backend = std::make_unique(Settings::values.web_api_url, - Settings::values.citra_username, - Settings::values.citra_token); -#else - backend = std::make_unique(); -#endif // Log one-time top-level information AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); @@ -192,9 +185,15 @@ TelemetrySession::~TelemetrySession() { .count()}; AddField(Telemetry::FieldType::Session, "Shutdown_Time", shutdown_time); +#ifdef ENABLE_WEB_SERVICE + auto backend = std::make_unique(Settings::values.web_api_url, + Settings::values.citra_username, + Settings::values.citra_token); +#else + auto backend = std::make_unique(); +#endif + // Complete the session, submitting to web service if necessary - // This is just a placeholder to wrap up the session once the core completes and this is - // destroyed. This will be moved elsewhere once we are actually doing real I/O with the service. field_collection.Accept(*backend); if (Settings::values.enable_telemetry) backend->Complete(); @@ -203,6 +202,9 @@ TelemetrySession::~TelemetrySession() { bool TelemetrySession::SubmitTestcase() { #ifdef ENABLE_WEB_SERVICE + auto backend = std::make_unique(Settings::values.web_api_url, + Settings::values.citra_username, + Settings::values.citra_token); field_collection.Accept(*backend); return backend->SubmitTestcase(); #else diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 12bb04c43..b3637e723 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -39,7 +39,6 @@ public: private: Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session - std::unique_ptr backend; ///< Backend interface that logs fields }; /** From 36051204cc24eafa96aef97f5bbe323d473227ac Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Fri, 19 Apr 2019 22:55:49 +0800 Subject: [PATCH 2/5] core, web_service: Check for error when registering rooms The `Register()` function can now handle error results and the error will be passed immediately to the Qt frontend, instead of being ignored silently and failing later with a "Room is not registered". --- src/citra_qt/multiplayer/host_room.cpp | 18 ++++++++++- src/citra_qt/multiplayer/state.cpp | 13 +++----- src/common/announce_multiplayer_room.h | 9 +++--- src/core/announce_multiplayer_session.cpp | 39 ++++++++++++++++------- src/core/announce_multiplayer_session.h | 7 ++-- src/web_service/announce_room_json.cpp | 12 +++---- src/web_service/announce_room_json.h | 2 +- 7 files changed, 67 insertions(+), 33 deletions(-) diff --git a/src/citra_qt/multiplayer/host_room.cpp b/src/citra_qt/multiplayer/host_room.cpp index 2dbc74d6c..2a815fa2c 100644 --- a/src/citra_qt/multiplayer/host_room.cpp +++ b/src/citra_qt/multiplayer/host_room.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -148,7 +149,22 @@ void HostRoomWindow::Host() { if (is_public) { if (auto session = announce_multiplayer_session.lock()) { // Register the room first to ensure verify_UID is present when we connect - session->Register(); + Common::WebResult result = session->Register(); + if (result.result_code != Common::WebResult::Code::Success) { + QMessageBox::warning( + this, tr("Error"), + tr("Failed to announce the room to the public lobby. In order to host a " + "room publicly, you must have a valid Citra account configured in " + "Emulation -> Configure -> Web. If you do not want to publish a room in " + "the public lobby, then select Unlisted instead.\nDebug Message: ") + + QString::fromStdString(result.result_string), + QMessageBox::Ok); + ui->host->setEnabled(true); + if (auto room = Network::GetRoom().lock()) { + room->Destroy(); + } + return; + } session->Start(); } else { LOG_ERROR(Network, "Starting announce session failed"); diff --git a/src/citra_qt/multiplayer/state.cpp b/src/citra_qt/multiplayer/state.cpp index 121d76b90..809bcddca 100644 --- a/src/citra_qt/multiplayer/state.cpp +++ b/src/citra_qt/multiplayer/state.cpp @@ -174,14 +174,11 @@ void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) { void MultiplayerState::OnAnnounceFailed(const Common::WebResult& result) { announce_multiplayer_session->Stop(); - QMessageBox::warning( - this, tr("Error"), - tr("Failed to announce the room to the public lobby. In order to host a room publicly, you " - "must have a valid Citra account configured in Emulation -> Configure -> Web. If you do " - "not want to publish a room in the public lobby, then select Unlisted instead.\n" - "Debug Message: ") + - QString::fromStdString(result.result_string), - QMessageBox::Ok); + QMessageBox::warning(this, tr("Error"), + tr("Failed to update the room information. Please check your Internet " + "connection and try hosting the room again.\nDebug Message: ") + + QString::fromStdString(result.result_string), + QMessageBox::Ok); } void MultiplayerState::UpdateThemedIcons() { diff --git a/src/common/announce_multiplayer_room.h b/src/common/announce_multiplayer_room.h index f0cf79386..b6e94b7a2 100644 --- a/src/common/announce_multiplayer_room.h +++ b/src/common/announce_multiplayer_room.h @@ -83,9 +83,10 @@ public: /** * Registers the data in the announce service - * @result A global Guid of the room which may be used for verification + * @result The result of the register attempt. When the result code is Success, A global Guid of + * the room which may be used for verification will be in the result's returned_data. */ - virtual std::string Register() = 0; + virtual Common::WebResult Register() = 0; /** * Empties the stored players @@ -121,8 +122,8 @@ public: Common::WebResult Update() override { return Common::WebResult{Common::WebResult::Code::NoWebservice, "WebService is missing"}; } - std::string Register() override { - return ""; + Common::WebResult Register() override { + return Common::WebResult{Common::WebResult::Code::NoWebservice, "WebService is missing"}; } void ClearPlayers() override {} RoomList GetRoomList() override { diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index afe522c25..90293ec92 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -29,19 +29,23 @@ AnnounceMultiplayerSession::AnnounceMultiplayerSession() { #endif } -void AnnounceMultiplayerSession::Register() { +Common::WebResult AnnounceMultiplayerSession::Register() { std::shared_ptr room = Network::GetRoom().lock(); if (!room) { - return; + return Common::WebResult{Common::WebResult::Code::LibError, "Network is not initialized"}; } if (room->GetState() != Network::Room::State::Open) { - return; + return Common::WebResult{Common::WebResult::Code::LibError, "Room is not open"}; } UpdateBackendData(room); - std::string result = backend->Register(); + Common::WebResult result = backend->Register(); + if (result.result_code != Common::WebResult::Code::Success) { + return result; + } LOG_INFO(WebService, "Room has been registered"); - room->SetVerifyUID(result); + room->SetVerifyUID(result.returned_data); registered = true; + return Common::WebResult{Common::WebResult::Code::Success}; } void AnnounceMultiplayerSession::Start() { @@ -95,9 +99,22 @@ void AnnounceMultiplayerSession::UpdateBackendData(std::shared_ptr lock(callback_mutex); + for (auto callback : error_callbacks) { + (*callback)(result); + } + }; + if (!registered) { - Register(); + Common::WebResult result = Register(); + if (result.result_code != Common::WebResult::Code::Success) { + ErrorCallback(result); + return; + } } + auto update_time = std::chrono::steady_clock::now(); std::future future; while (!shutdown_event.WaitUntil(update_time)) { @@ -112,15 +129,15 @@ void AnnounceMultiplayerSession::AnnounceMultiplayerLoop() { UpdateBackendData(room); Common::WebResult result = backend->Update(); if (result.result_code != Common::WebResult::Code::Success) { - std::lock_guard lock(callback_mutex); - for (auto callback : error_callbacks) { - (*callback)(result); - } + ErrorCallback(result); } if (result.result_string == "404") { registered = false; // Needs to register the room again - Register(); + Common::WebResult result = Register(); + if (result.result_code != Common::WebResult::Code::Success) { + ErrorCallback(result); + } } } } diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h index 79b30fb24..07e09a448 100644 --- a/src/core/announce_multiplayer_session.h +++ b/src/core/announce_multiplayer_session.h @@ -44,8 +44,11 @@ public: */ void UnbindErrorCallback(CallbackHandle handle); - /// Registers a room to web services - void Register(); + /** + * Registers a room to web services + * @return The result of the registration attempt. + */ + Common::WebResult Register(); /** * Starts the announce of a room to web services diff --git a/src/web_service/announce_room_json.cpp b/src/web_service/announce_room_json.cpp index 2d2601256..44d0b0578 100644 --- a/src/web_service/announce_room_json.cpp +++ b/src/web_service/announce_room_json.cpp @@ -117,16 +117,16 @@ Common::WebResult RoomJson::Update() { return client.PostJson(fmt::format("/lobby/{}", room_id), json.dump(), false); } -std::string RoomJson::Register() { +Common::WebResult RoomJson::Register() { nlohmann::json json = room; - auto reply = client.PostJson("/lobby", json.dump(), false).returned_data; - if (reply.empty()) { - return ""; + auto result = client.PostJson("/lobby", json.dump(), false); + if (result.result_code != Common::WebResult::Code::Success) { + return result; } - auto reply_json = nlohmann::json::parse(reply); + auto reply_json = nlohmann::json::parse(result.returned_data); room = reply_json.get(); room_id = reply_json.at("id").get(); - return room.verify_UID; + return Common::WebResult{Common::WebResult::Code::Success, "", room.verify_UID}; } void RoomJson::ClearPlayers() { diff --git a/src/web_service/announce_room_json.h b/src/web_service/announce_room_json.h index 16d630863..b87af60eb 100644 --- a/src/web_service/announce_room_json.h +++ b/src/web_service/announce_room_json.h @@ -29,7 +29,7 @@ public: const AnnounceMultiplayerRoom::MacAddress& mac_address, const u64 game_id, const std::string& game_name) override; Common::WebResult Update() override; - std::string Register() override; + Common::WebResult Register() override; void ClearPlayers() override; AnnounceMultiplayerRoom::RoomList GetRoomList() override; void Delete() override; From d6b168d7edcb2bf8a9c440f46ff55d93498b890a Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Fri, 19 Apr 2019 23:12:30 +0800 Subject: [PATCH 3/5] citra_qt/multiplayer: Disallow hosting rooms when preferred game is not selected which can happen if the game list is empty, and would result in a 400 being returned from the server as the preferred_game field is empty. --- src/citra_qt/multiplayer/host_room.cpp | 4 ++++ src/citra_qt/multiplayer/message.cpp | 3 +++ src/citra_qt/multiplayer/message.h | 1 + 3 files changed, 8 insertions(+) diff --git a/src/citra_qt/multiplayer/host_room.cpp b/src/citra_qt/multiplayer/host_room.cpp index 2a815fa2c..8abe84f92 100644 --- a/src/citra_qt/multiplayer/host_room.cpp +++ b/src/citra_qt/multiplayer/host_room.cpp @@ -111,6 +111,10 @@ void HostRoomWindow::Host() { NetworkMessage::ShowError(NetworkMessage::PORT_NOT_VALID); return; } + if (ui->game_list->currentIndex() == -1) { + NetworkMessage::ShowError(NetworkMessage::GAME_NOT_SELECTED); + return; + } if (auto member = Network::GetRoomMember().lock()) { if (member->GetState() == Network::RoomMember::State::Joining) { return; diff --git a/src/citra_qt/multiplayer/message.cpp b/src/citra_qt/multiplayer/message.cpp index 36e465acb..28d692c1e 100644 --- a/src/citra_qt/multiplayer/message.cpp +++ b/src/citra_qt/multiplayer/message.cpp @@ -16,6 +16,9 @@ const ConnectionError USERNAME_NOT_VALID_SERVER( QT_TR_NOOP("Username is already in use or not valid. Please choose another.")); const ConnectionError IP_ADDRESS_NOT_VALID(QT_TR_NOOP("IP is not a valid IPv4 address.")); const ConnectionError PORT_NOT_VALID(QT_TR_NOOP("Port must be a number between 0 to 65535.")); +const ConnectionError GAME_NOT_SELECTED(QT_TR_NOOP( + "You must choose a Preferred Game to host a room. If you do not have any games in your game " + "list yet, add a game folder by clicking on the plus icon in the game list.")); const ConnectionError NO_INTERNET( QT_TR_NOOP("Unable to find an internet connection. Check your internet settings.")); const ConnectionError UNABLE_TO_CONNECT( diff --git a/src/citra_qt/multiplayer/message.h b/src/citra_qt/multiplayer/message.h index 955b90847..7b1caa5fd 100644 --- a/src/citra_qt/multiplayer/message.h +++ b/src/citra_qt/multiplayer/message.h @@ -27,6 +27,7 @@ extern const ConnectionError ROOMNAME_NOT_VALID; extern const ConnectionError USERNAME_NOT_VALID_SERVER; extern const ConnectionError IP_ADDRESS_NOT_VALID; extern const ConnectionError PORT_NOT_VALID; +extern const ConnectionError GAME_NOT_SELECTED; extern const ConnectionError NO_INTERNET; extern const ConnectionError UNABLE_TO_CONNECT; extern const ConnectionError ROOM_IS_FULL; From 4a9ea65e496f1c592682411fef377d70dd2050af Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sat, 20 Apr 2019 10:42:20 +0800 Subject: [PATCH 4/5] core, citra_qt: Allow credentials update in multiplayer announce session Allows updating the credentials of the announce session, thus allowing credentials changes to be reflected before citra restart. To avoid race conditions and web errors (you can only update the room that you created, i.e. changing credentials halfway will make it break), now you can only use the Citra Web Services settings when not hosting a public room. --- src/citra_qt/configuration/configure_dialog.cpp | 3 ++- src/citra_qt/configuration/configure_dialog.h | 3 ++- src/citra_qt/configuration/configure_web.cpp | 5 +++++ src/citra_qt/configuration/configure_web.h | 1 + src/citra_qt/configuration/configure_web.ui | 10 ++++++++++ src/citra_qt/main.cpp | 5 ++++- src/citra_qt/multiplayer/state.cpp | 8 ++++++++ src/citra_qt/multiplayer/state.h | 8 ++++++++ src/core/announce_multiplayer_session.cpp | 14 ++++++++++++++ src/core/announce_multiplayer_session.h | 11 +++++++++++ 10 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/citra_qt/configuration/configure_dialog.cpp b/src/citra_qt/configuration/configure_dialog.cpp index e1fc5abac..653f2a387 100644 --- a/src/citra_qt/configuration/configure_dialog.cpp +++ b/src/citra_qt/configuration/configure_dialog.cpp @@ -10,10 +10,11 @@ #include "core/settings.h" #include "ui_configure.h" -ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry) +ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, bool enable_web_config) : QDialog(parent), registry(registry), ui(new Ui::ConfigureDialog) { ui->setupUi(this); ui->hotkeysTab->Populate(registry); + ui->webTab->SetWebServiceConfigEnabled(enable_web_config); this->PopulateSelectionList(); connect(ui->uiTab, &ConfigureUi::languageChanged, this, &ConfigureDialog::onLanguageChanged); diff --git a/src/citra_qt/configuration/configure_dialog.h b/src/citra_qt/configuration/configure_dialog.h index 91234ca0b..6fab7cff1 100644 --- a/src/citra_qt/configuration/configure_dialog.h +++ b/src/citra_qt/configuration/configure_dialog.h @@ -17,7 +17,8 @@ class ConfigureDialog : public QDialog { Q_OBJECT public: - explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry); + explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry, + bool enable_web_config = true); ~ConfigureDialog() override; void applyConfiguration(); diff --git a/src/citra_qt/configuration/configure_web.cpp b/src/citra_qt/configuration/configure_web.cpp index 56f876524..e8a0cc21f 100644 --- a/src/citra_qt/configuration/configure_web.cpp +++ b/src/citra_qt/configuration/configure_web.cpp @@ -118,3 +118,8 @@ void ConfigureWeb::OnLoginVerified() { void ConfigureWeb::retranslateUi() { ui->retranslateUi(this); } + +void ConfigureWeb::SetWebServiceConfigEnabled(bool enabled) { + ui->label_disable_info->setVisible(!enabled); + ui->groupBoxWebConfig->setEnabled(enabled); +} diff --git a/src/citra_qt/configuration/configure_web.h b/src/citra_qt/configuration/configure_web.h index d52bab7a0..2e0ab2e9b 100644 --- a/src/citra_qt/configuration/configure_web.h +++ b/src/citra_qt/configuration/configure_web.h @@ -22,6 +22,7 @@ public: void applyConfiguration(); void retranslateUi(); void setConfiguration(); + void SetWebServiceConfigEnabled(bool enabled); private: void RefreshTelemetryID(); diff --git a/src/citra_qt/configuration/configure_web.ui b/src/citra_qt/configuration/configure_web.ui index a8aeda9ad..ab3559157 100644 --- a/src/citra_qt/configuration/configure_web.ui +++ b/src/citra_qt/configuration/configure_web.ui @@ -118,6 +118,16 @@ + + + + Web Service configuration can only be changed when a public room isn't being hosted. + + + true + + + diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index bcff0b9fc..3c2a9f5f5 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -1335,7 +1335,8 @@ void GMainWindow::OnCheats() { } void GMainWindow::OnConfigure() { - ConfigureDialog configureDialog(this, hotkey_registry); + ConfigureDialog configureDialog(this, hotkey_registry, + !multiplayer_state->IsHostingPublicRoom()); connect(&configureDialog, &ConfigureDialog::languageChanged, this, &GMainWindow::OnLanguageChanged); auto old_theme = UISettings::values.theme; @@ -1350,6 +1351,8 @@ void GMainWindow::OnConfigure() { UpdateUITheme(); if (UISettings::values.enable_discord_presence != old_discord_presence) SetDiscordEnabled(UISettings::values.enable_discord_presence); + if (!multiplayer_state->IsHostingPublicRoom()) + multiplayer_state->UpdateCredentials(); emit UpdateThemedIcons(); SyncMenuUISettings(); game_list->RefreshGameDirectory(); diff --git a/src/citra_qt/multiplayer/state.cpp b/src/citra_qt/multiplayer/state.cpp index 809bcddca..c122a5c83 100644 --- a/src/citra_qt/multiplayer/state.cpp +++ b/src/citra_qt/multiplayer/state.cpp @@ -278,3 +278,11 @@ void MultiplayerState::OnDirectConnectToRoom() { } BringWidgetToFront(direct_connect); } + +bool MultiplayerState::IsHostingPublicRoom() const { + return announce_multiplayer_session->IsRunning(); +} + +void MultiplayerState::UpdateCredentials() { + announce_multiplayer_session->UpdateCredentials(); +} diff --git a/src/citra_qt/multiplayer/state.h b/src/citra_qt/multiplayer/state.h index 8061d18cf..78a5530dd 100644 --- a/src/citra_qt/multiplayer/state.h +++ b/src/citra_qt/multiplayer/state.h @@ -38,6 +38,14 @@ public: void retranslateUi(); + /** + * Whether a public room is being hosted or not. + * When this is true, Web Services configuration should be disabled. + */ + bool IsHostingPublicRoom() const; + + void UpdateCredentials(); + public slots: void OnNetworkStateChanged(const Network::RoomMember::State& state); void OnNetworkError(const Network::RoomMember::Error& error); diff --git a/src/core/announce_multiplayer_session.cpp b/src/core/announce_multiplayer_session.cpp index 90293ec92..d46c8db56 100644 --- a/src/core/announce_multiplayer_session.cpp +++ b/src/core/announce_multiplayer_session.cpp @@ -146,4 +146,18 @@ AnnounceMultiplayerRoom::RoomList AnnounceMultiplayerSession::GetRoomList() { return backend->GetRoomList(); } +bool AnnounceMultiplayerSession::IsRunning() const { + return announce_multiplayer_thread != nullptr; +} + +void AnnounceMultiplayerSession::UpdateCredentials() { + ASSERT_MSG(!IsRunning(), "Credentials can only be updated when session is not running"); + +#ifdef ENABLE_WEB_SERVICE + backend = std::make_unique(Settings::values.web_api_url, + Settings::values.citra_username, + Settings::values.citra_token); +#endif +} + } // namespace Core diff --git a/src/core/announce_multiplayer_session.h b/src/core/announce_multiplayer_session.h index 07e09a448..5647972d0 100644 --- a/src/core/announce_multiplayer_session.h +++ b/src/core/announce_multiplayer_session.h @@ -67,6 +67,17 @@ public: */ AnnounceMultiplayerRoom::RoomList GetRoomList(); + /** + * Whether the announce session is still running + */ + bool IsRunning() const; + + /** + * Recreates the backend, updating the credentials. + * This can only be used when the announce session is not running. + */ + void UpdateCredentials(); + private: Common::Event shutdown_event; std::mutex callback_mutex; From 40ad54c5c7cb1d47e36f82db1a3bdd8616a1464f Mon Sep 17 00:00:00 2001 From: zhupengfei Date: Sat, 20 Apr 2019 12:38:19 +0800 Subject: [PATCH 5/5] citra_qt: Update the multiplayer dialogs when game list is repopulated The multiplayer state object and dialogs hold a (modified) game list model, but it isn't updated when the actual game list changes. This updates the multiplayer dialogs with the new game list when it got repopulated. --- src/citra_qt/game_list.cpp | 2 ++ src/citra_qt/game_list.h | 1 + src/citra_qt/main.cpp | 2 ++ src/citra_qt/multiplayer/host_room.cpp | 18 +++++++++++------- src/citra_qt/multiplayer/host_room.h | 5 +++++ src/citra_qt/multiplayer/lobby.cpp | 24 +++++++++++++++++------- src/citra_qt/multiplayer/lobby.h | 18 +++++++++++++++--- src/citra_qt/multiplayer/state.cpp | 10 ++++++++++ src/citra_qt/multiplayer/state.h | 6 ++++++ 9 files changed, 69 insertions(+), 17 deletions(-) diff --git a/src/citra_qt/game_list.cpp b/src/citra_qt/game_list.cpp index 4ab491f04..dad0a3cdb 100644 --- a/src/citra_qt/game_list.cpp +++ b/src/citra_qt/game_list.cpp @@ -421,6 +421,8 @@ void GameList::DonePopulating(QStringList watch_list) { if (childrenTotal > 0) { search_field->setFocus(); } + + emit PopulatingCompleted(); } void GameList::PopupContextMenu(const QPoint& menu_location) { diff --git a/src/citra_qt/game_list.h b/src/citra_qt/game_list.h index a10d7fe15..ccf695fa5 100644 --- a/src/citra_qt/game_list.h +++ b/src/citra_qt/game_list.h @@ -76,6 +76,7 @@ signals: void OpenDirectory(QString directory); void AddDirectory(); void ShowList(bool show); + void PopulatingCompleted(); private slots: void onItemExpanded(const QModelIndex& item); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 3c2a9f5f5..055627d47 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -506,6 +506,8 @@ void GMainWindow::ConnectWidgetEvents() { connect(game_list_placeholder, &GameListPlaceholder::AddDirectory, this, &GMainWindow::OnGameListAddDirectory); connect(game_list, &GameList::ShowList, this, &GMainWindow::OnGameListShowList); + connect(game_list, &GameList::PopulatingCompleted, + [this] { multiplayer_state->UpdateGameList(game_list->GetModel()); }); connect(this, &GMainWindow::EmulationStarting, render_window, &GRenderWindow::OnEmulationStarting); diff --git a/src/citra_qt/multiplayer/host_room.cpp b/src/citra_qt/multiplayer/host_room.cpp index 8abe84f92..05e696a93 100644 --- a/src/citra_qt/multiplayer/host_room.cpp +++ b/src/citra_qt/multiplayer/host_room.cpp @@ -41,13 +41,7 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, // Create a proxy to the game list to display the list of preferred games game_list = new QStandardItemModel; - - for (int i = 0; i < list->rowCount(); i++) { - auto parent = list->item(i, 0); - for (int j = 0; j < parent->rowCount(); j++) { - game_list->appendRow(parent->child(j)->clone()); - } - } + UpdateGameList(list); proxy = new ComboBoxProxyModel; proxy->setSourceModel(game_list); @@ -79,6 +73,16 @@ HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, HostRoomWindow::~HostRoomWindow() = default; +void HostRoomWindow::UpdateGameList(QStandardItemModel* list) { + game_list->clear(); + for (int i = 0; i < list->rowCount(); i++) { + auto parent = list->item(i, 0); + for (int j = 0; j < parent->rowCount(); j++) { + game_list->appendRow(parent->child(j)->clone()); + } + } +} + void HostRoomWindow::RetranslateUi() { ui->retranslateUi(this); } diff --git a/src/citra_qt/multiplayer/host_room.h b/src/citra_qt/multiplayer/host_room.h index 620574bd6..247028383 100644 --- a/src/citra_qt/multiplayer/host_room.h +++ b/src/citra_qt/multiplayer/host_room.h @@ -38,6 +38,11 @@ public: std::shared_ptr session); ~HostRoomWindow(); + /** + * Updates the dialog with a new game list model. + * This model should be the original model of the game list. + */ + void UpdateGameList(QStandardItemModel* list); void RetranslateUi(); private: diff --git a/src/citra_qt/multiplayer/lobby.cpp b/src/citra_qt/multiplayer/lobby.cpp index 3b48b3a95..c06738565 100644 --- a/src/citra_qt/multiplayer/lobby.cpp +++ b/src/citra_qt/multiplayer/lobby.cpp @@ -35,13 +35,7 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, // Create a proxy to the game list to get the list of games owned game_list = new QStandardItemModel; - - for (int i = 0; i < list->rowCount(); i++) { - auto parent = list->item(i, 0); - for (int j = 0; j < parent->rowCount(); j++) { - game_list->appendRow(parent->child(j)->clone()); - } - } + UpdateGameList(list); proxy = new LobbyFilterProxyModel(this, game_list); proxy->setSourceModel(model); @@ -88,6 +82,18 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, RefreshLobby(); } +void Lobby::UpdateGameList(QStandardItemModel* list) { + game_list->clear(); + for (int i = 0; i < list->rowCount(); i++) { + auto parent = list->item(i, 0); + for (int j = 0; j < parent->rowCount(); j++) { + game_list->appendRow(parent->child(j)->clone()); + } + } + if (proxy) + proxy->UpdateGameList(game_list); +} + void Lobby::RetranslateUi() { ui->retranslateUi(this); } @@ -260,6 +266,10 @@ void Lobby::OnRefreshLobby() { LobbyFilterProxyModel::LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list) : QSortFilterProxyModel(parent), game_list(list) {} +void LobbyFilterProxyModel::UpdateGameList(QStandardItemModel* list) { + game_list = list; +} + bool LobbyFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const { // Prioritize filters by fastest to compute diff --git a/src/citra_qt/multiplayer/lobby.h b/src/citra_qt/multiplayer/lobby.h index 9d77b73b6..0b1d85b53 100644 --- a/src/citra_qt/multiplayer/lobby.h +++ b/src/citra_qt/multiplayer/lobby.h @@ -30,6 +30,11 @@ public: std::shared_ptr session); ~Lobby() = default; + /** + * Updates the lobby with a new game list model. + * This model should be the original model of the game list. + */ + void UpdateGameList(QStandardItemModel* list); void RetranslateUi(); public slots: @@ -76,9 +81,9 @@ private: */ QString PasswordPrompt(); - QStandardItemModel* model; - QStandardItemModel* game_list; - LobbyFilterProxyModel* proxy; + QStandardItemModel* model{}; + QStandardItemModel* game_list{}; + LobbyFilterProxyModel* proxy{}; QFutureWatcher room_list_watcher; std::weak_ptr announce_multiplayer_session; @@ -95,6 +100,13 @@ class LobbyFilterProxyModel : public QSortFilterProxyModel { public: explicit LobbyFilterProxyModel(QWidget* parent, QStandardItemModel* list); + + /** + * Updates the filter with a new game list model. + * This model should be the processed one created by the Lobby. + */ + void UpdateGameList(QStandardItemModel* list); + bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override; void sort(int column, Qt::SortOrder order) override; diff --git a/src/citra_qt/multiplayer/state.cpp b/src/citra_qt/multiplayer/state.cpp index c122a5c83..4daafd3a0 100644 --- a/src/citra_qt/multiplayer/state.cpp +++ b/src/citra_qt/multiplayer/state.cpp @@ -286,3 +286,13 @@ bool MultiplayerState::IsHostingPublicRoom() const { void MultiplayerState::UpdateCredentials() { announce_multiplayer_session->UpdateCredentials(); } + +void MultiplayerState::UpdateGameList(QStandardItemModel* game_list) { + game_list_model = game_list; + if (lobby) { + lobby->UpdateGameList(game_list); + } + if (host_room) { + host_room->UpdateGameList(game_list); + } +} diff --git a/src/citra_qt/multiplayer/state.h b/src/citra_qt/multiplayer/state.h index 78a5530dd..707efd5a4 100644 --- a/src/citra_qt/multiplayer/state.h +++ b/src/citra_qt/multiplayer/state.h @@ -46,6 +46,12 @@ public: void UpdateCredentials(); + /** + * Updates the multiplayer dialogs with a new game list model. + * This model should be the original model of the game list. + */ + void UpdateGameList(QStandardItemModel* game_list); + public slots: void OnNetworkStateChanged(const Network::RoomMember::State& state); void OnNetworkError(const Network::RoomMember::Error& error);