| | |
| | |
| |
|
| | #include <QAction> |
| | #include <QApplication> |
| | #include <QIcon> |
| | #include <QMessageBox> |
| | #include <QStandardItemModel> |
| | #include "common/announce_multiplayer_room.h" |
| | #include "common/logging/log.h" |
| | #include "core/core.h" |
| | #include "yuzu/game_list.h" |
| | #include "yuzu/multiplayer/client_room.h" |
| | #include "yuzu/multiplayer/direct_connect.h" |
| | #include "yuzu/multiplayer/host_room.h" |
| | #include "yuzu/multiplayer/lobby.h" |
| | #include "yuzu/multiplayer/message.h" |
| | #include "yuzu/multiplayer/state.h" |
| | #include "yuzu/uisettings.h" |
| | #include "yuzu/util/clickable_label.h" |
| |
|
| | MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_, |
| | QAction* leave_room_, QAction* show_room_, Core::System& system_) |
| | : QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_), |
| | show_room(show_room_), system{system_}, room_network{system.GetRoomNetwork()} { |
| | if (auto member = room_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<WebService::WebResult>(); |
| | announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>(room_network); |
| | announce_multiplayer_session->BindErrorCallback( |
| | [this](const WebService::WebResult& result) { emit AnnounceFailed(result); }); |
| | connect(this, &MultiplayerState::AnnounceFailed, this, &MultiplayerState::OnAnnounceFailed); |
| |
|
| | status_text = new ClickableLabel(this); |
| | status_icon = new ClickableLabel(this); |
| |
|
| | 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(); |
| | } |
| | }); |
| |
|
| | retranslateUi(); |
| | } |
| |
|
| | MultiplayerState::~MultiplayerState() = default; |
| |
|
| | void MultiplayerState::Close() { |
| | if (state_callback_handle) { |
| | if (auto member = room_network.GetRoomMember().lock()) { |
| | member->Unbind(state_callback_handle); |
| | } |
| | } |
| |
|
| | if (error_callback_handle) { |
| | if (auto member = room_network.GetRoomMember().lock()) { |
| | member->Unbind(error_callback_handle); |
| | } |
| | } |
| | 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")); |
| |
|
| | UpdateNotificationStatus(); |
| |
|
| | if (lobby) { |
| | lobby->RetranslateUi(); |
| | } |
| | if (host_room) { |
| | host_room->RetranslateUi(); |
| | } |
| | if (client_room) { |
| | client_room->RetranslateUi(); |
| | } |
| | if (direct_connect) { |
| | direct_connect->RetranslateUi(); |
| | } |
| | } |
| |
|
| | void MultiplayerState::SetNotificationStatus(NotificationStatus status) { |
| | notification_status = status; |
| | UpdateNotificationStatus(); |
| | } |
| |
|
| | void MultiplayerState::UpdateNotificationStatus() { |
| | switch (notification_status) { |
| | case NotificationStatus::Uninitialized: |
| | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); |
| | status_text->setText(tr("Not Connected. Click here to find a room!")); |
| | leave_room->setEnabled(false); |
| | show_room->setEnabled(false); |
| | break; |
| | case NotificationStatus::Disconnected: |
| | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16)); |
| | status_text->setText(tr("Not Connected")); |
| | leave_room->setEnabled(false); |
| | show_room->setEnabled(false); |
| | break; |
| | case NotificationStatus::Connected: |
| | status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16)); |
| | status_text->setText(tr("Connected")); |
| | leave_room->setEnabled(true); |
| | show_room->setEnabled(true); |
| | break; |
| | case NotificationStatus::Notification: |
| | status_icon->setPixmap( |
| | QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16)); |
| | status_text->setText(tr("New Messages Received")); |
| | leave_room->setEnabled(true); |
| | show_room->setEnabled(true); |
| | break; |
| | } |
| |
|
| | |
| | if (system.IsPoweredOn()) { |
| | status_text->clear(); |
| | } |
| | } |
| |
|
| | 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(); |
| | SetNotificationStatus(NotificationStatus::Connected); |
| | } else { |
| | SetNotificationStatus(NotificationStatus::Disconnected); |
| | } |
| |
|
| | 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::IpCollision: |
| | NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_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 WebService::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::OnSaveConfig() { |
| | emit SaveConfig(); |
| | } |
| |
|
| | 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(this, game_list_model, announce_multiplayer_session, system); |
| | connect(lobby, &Lobby::SaveConfig, this, &MultiplayerState::OnSaveConfig); |
| | } |
| | lobby->RefreshLobby(); |
| | BringWidgetToFront(lobby); |
| | } |
| |
|
| | void MultiplayerState::OnCreateRoom() { |
| | if (host_room == nullptr) { |
| | host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); |
| | connect(host_room, &HostRoomWindow::SaveConfig, this, &MultiplayerState::OnSaveConfig); |
| | } |
| | BringWidgetToFront(host_room); |
| | } |
| |
|
| | bool MultiplayerState::OnCloseRoom() { |
| | if (!NetworkMessage::WarnCloseRoom()) |
| | return false; |
| | if (auto room = room_network.GetRoom().lock()) { |
| | |
| | if (auto member = room_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.multiplayer_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); |
| | QApplication::beep(); |
| | SetNotificationStatus(NotificationStatus::Notification); |
| | } |
| |
|
| | void MultiplayerState::HideNotification() { |
| | show_notification = false; |
| | SetNotificationStatus(NotificationStatus::Connected); |
| | } |
| |
|
| | void MultiplayerState::OnOpenNetworkRoom() { |
| | if (auto member = room_network.GetRoomMember().lock()) { |
| | if (member->IsConnected()) { |
| | if (client_room == nullptr) { |
| | client_room = new ClientRoomWindow(this, room_network); |
| | 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); |
| | connect(direct_connect, &DirectConnectWindow::SaveConfig, this, |
| | &MultiplayerState::OnSaveConfig); |
| | } |
| | 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); |
| | } |
| | } |
| |
|