| |
| |
| |
|
|
| #include <QAction> |
| #include <QApplication> |
| #include <QIcon> |
| #include <QMessageBox> |
| #include <QStandardItemModel> |
| #include "citra_qt/multiplayer/client_room.h" |
| #include "citra_qt/multiplayer/direct_connect.h" |
| #include "citra_qt/multiplayer/host_room.h" |
| #include "citra_qt/multiplayer/lobby.h" |
| #include "citra_qt/multiplayer/message.h" |
| #include "citra_qt/multiplayer/state.h" |
| #include "citra_qt/uisettings.h" |
| #include "citra_qt/util/clickable_label.h" |
| #include "common/logging/log.h" |
|
|
| MultiplayerState::MultiplayerState(Core::System& system_, QWidget* parent, |
| QStandardItemModel* game_list_model, QAction* leave_room, |
| QAction* show_room) |
| : QWidget(parent), system{system_}, game_list_model(game_list_model), leave_room(leave_room), |
| show_room(show_room) { |
| if (auto member = Network::GetRoomMember().lock()) { |
| |
| state_callback_handle = member->BindOnStateChanged( |
| [this](const Network::RoomMember::State& state) { emit NetworkStateChanged(state); }); |
| connect(this, &MultiplayerState::NetworkStateChanged, this, |
| &MultiplayerState::OnNetworkStateChanged); |
| error_callback_handle = member->BindOnError( |
| [this](const Network::RoomMember::Error& error) { emit NetworkError(error); }); |
| connect(this, &MultiplayerState::NetworkError, this, &MultiplayerState::OnNetworkError); |
| } |
|
|
| qRegisterMetaType<Network::RoomMember::State>(); |
| qRegisterMetaType<Network::RoomMember::Error>(); |
| qRegisterMetaType<Common::WebResult>(); |
| announce_multiplayer_session = std::make_shared<Network::AnnounceMultiplayerSession>(); |
| announce_multiplayer_session->BindErrorCallback( |
| [this](const Common::WebResult& result) { emit AnnounceFailed(result); }); |
| connect(this, &MultiplayerState::AnnounceFailed, this, &MultiplayerState::OnAnnounceFailed); |
|
|
| status_text = new ClickableLabel(this); |
| status_icon = new ClickableLabel(this); |
| status_text->setToolTip(tr("Current connection status")); |
| status_text->setText(tr("Not Connected. Click here to find a room!")); |
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); |
|
|
| connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); |
| connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom); |
|
|
| connect(static_cast<QApplication*>(QApplication::instance()), &QApplication::focusChanged, this, |
| [this](QWidget* , QWidget* now) { |
| if (client_room && client_room->isAncestorOf(now)) { |
| HideNotification(); |
| } |
| }); |
| } |
|
|
| MultiplayerState::~MultiplayerState() { |
| if (state_callback_handle) { |
| if (auto member = Network::GetRoomMember().lock()) { |
| member->Unbind(state_callback_handle); |
| } |
| } |
|
|
| if (error_callback_handle) { |
| if (auto member = Network::GetRoomMember().lock()) { |
| member->Unbind(error_callback_handle); |
| } |
| } |
| } |
|
|
| void MultiplayerState::Close() { |
| if (host_room) |
| host_room->close(); |
| if (direct_connect) |
| direct_connect->close(); |
| if (client_room) |
| client_room->close(); |
| if (lobby) |
| lobby->close(); |
| } |
|
|
| void MultiplayerState::retranslateUi() { |
| status_text->setToolTip(tr("Current connection status")); |
|
|
| if (current_state == Network::RoomMember::State::Uninitialized) { |
| status_text->setText(tr("Not Connected. Click here to find a room!")); |
| } else if (current_state == Network::RoomMember::State::Joined || |
| current_state == Network::RoomMember::State::Moderator) { |
|
|
| status_text->setText(tr("Connected")); |
| } else { |
| status_text->setText(tr("Not Connected")); |
| } |
|
|
| if (lobby) |
| lobby->RetranslateUi(); |
| if (host_room) |
| host_room->RetranslateUi(); |
| if (client_room) |
| client_room->RetranslateUi(); |
| if (direct_connect) |
| direct_connect->RetranslateUi(); |
| } |
|
|
| void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) { |
| LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state)); |
| if (state == Network::RoomMember::State::Joined || |
| state == Network::RoomMember::State::Moderator) { |
|
|
| OnOpenNetworkRoom(); |
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); |
| status_text->setText(tr("Connected")); |
| leave_room->setEnabled(true); |
| show_room->setEnabled(true); |
| } else { |
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); |
| status_text->setText(tr("Not Connected")); |
| leave_room->setEnabled(false); |
| show_room->setEnabled(false); |
| } |
|
|
| current_state = state; |
| } |
|
|
| void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) { |
| LOG_DEBUG(Frontend, "Network Error: {}", Network::GetErrorStr(error)); |
| switch (error) { |
| case Network::RoomMember::Error::LostConnection: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::LOST_CONNECTION); |
| break; |
| case Network::RoomMember::Error::HostKicked: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::HOST_KICKED); |
| break; |
| case Network::RoomMember::Error::CouldNotConnect: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::UNABLE_TO_CONNECT); |
| break; |
| case Network::RoomMember::Error::NameCollision: |
| NetworkMessage::ErrorManager::ShowError( |
| NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER); |
| break; |
| case Network::RoomMember::Error::MacCollision: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::MAC_COLLISION); |
| break; |
| case Network::RoomMember::Error::ConsoleIdCollision: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::CONSOLE_ID_COLLISION); |
| break; |
| case Network::RoomMember::Error::RoomIsFull: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL); |
| break; |
| case Network::RoomMember::Error::WrongPassword: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::WRONG_PASSWORD); |
| break; |
| case Network::RoomMember::Error::WrongVersion: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::WRONG_VERSION); |
| break; |
| case Network::RoomMember::Error::HostBanned: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::HOST_BANNED); |
| break; |
| case Network::RoomMember::Error::UnknownError: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::UNABLE_TO_CONNECT); |
| break; |
| case Network::RoomMember::Error::PermissionDenied: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PERMISSION_DENIED); |
| break; |
| case Network::RoomMember::Error::NoSuchUser: |
| NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::NO_SUCH_USER); |
| break; |
| } |
| } |
|
|
| void MultiplayerState::OnAnnounceFailed(const Common::WebResult& result) { |
| announce_multiplayer_session->Stop(); |
| 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() { |
| if (show_notification) { |
| status_icon->setPixmap( |
| QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); |
| } else if (current_state == Network::RoomMember::State::Joined || |
| current_state == Network::RoomMember::State::Moderator) { |
|
|
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); |
| } else { |
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); |
| } |
| if (client_room) |
| client_room->UpdateIconDisplay(); |
| } |
|
|
| static void BringWidgetToFront(QWidget* widget) { |
| widget->show(); |
| widget->activateWindow(); |
| widget->raise(); |
| } |
|
|
| void MultiplayerState::OnViewLobby() { |
| if (lobby == nullptr) { |
| lobby = new Lobby(system, this, game_list_model, announce_multiplayer_session); |
| } |
| BringWidgetToFront(lobby); |
| } |
|
|
| void MultiplayerState::OnCreateRoom() { |
| if (host_room == nullptr) { |
| host_room = new HostRoomWindow(system, this, game_list_model, announce_multiplayer_session); |
| } |
| BringWidgetToFront(host_room); |
| } |
|
|
| bool MultiplayerState::OnCloseRoom() { |
| if (!NetworkMessage::WarnCloseRoom()) |
| return false; |
| if (auto room = Network::GetRoom().lock()) { |
| |
| if (auto member = Network::GetRoomMember().lock()) { |
| member->Leave(); |
| LOG_DEBUG(Frontend, "Left the room (as a client)"); |
| } |
|
|
| |
| if (room->GetState() != Network::Room::State::Open) { |
| return true; |
| } |
|
|
| |
| UISettings::values.ban_list = room->GetBanList(); |
|
|
| room->Destroy(); |
| announce_multiplayer_session->Stop(); |
| LOG_DEBUG(Frontend, "Closed the room (as a server)"); |
| } |
| return true; |
| } |
|
|
| void MultiplayerState::ShowNotification() { |
| if (client_room && client_room->isAncestorOf(QApplication::focusWidget())) |
| return; |
| show_notification = true; |
| QApplication::alert(nullptr); |
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); |
| status_text->setText(tr("New Messages Received")); |
| } |
|
|
| void MultiplayerState::HideNotification() { |
| show_notification = false; |
| status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); |
| status_text->setText(tr("Connected")); |
| } |
|
|
| void MultiplayerState::OnOpenNetworkRoom() { |
| if (auto member = Network::GetRoomMember().lock()) { |
| if (member->IsConnected()) { |
| if (client_room == nullptr) { |
| client_room = new ClientRoomWindow(this); |
| connect(client_room, &ClientRoomWindow::ShowNotification, this, |
| &MultiplayerState::ShowNotification); |
| } |
| BringWidgetToFront(client_room); |
| return; |
| } |
| } |
| |
| |
| OnViewLobby(); |
| } |
|
|
| void MultiplayerState::OnDirectConnectToRoom() { |
| if (direct_connect == nullptr) { |
| direct_connect = new DirectConnectWindow(system, this); |
| } |
| BringWidgetToFront(direct_connect); |
| } |
|
|
| bool MultiplayerState::IsHostingPublicRoom() const { |
| return announce_multiplayer_session->IsRunning(); |
| } |
|
|
| 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); |
| } |
| } |
|
|