diff options
Diffstat (limited to 'src/yuzu/multiplayer/state.cpp')
-rw-r--r-- | src/yuzu/multiplayer/state.cpp | 299 |
1 files changed, 299 insertions, 0 deletions
diff --git a/src/yuzu/multiplayer/state.cpp b/src/yuzu/multiplayer/state.cpp new file mode 100644 index 000000000..b2aaed982 --- /dev/null +++ b/src/yuzu/multiplayer/state.cpp @@ -0,0 +1,299 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QAction> +#include <QApplication> +#include <QIcon> +#include <QMessageBox> +#include <QStandardItemModel> +#include "common/announce_multiplayer_room.h" +#include "common/logging/log.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) + : QWidget(parent), game_list_model(game_list_model), leave_room(leave_room), + show_room(show_room) { + if (auto member = Network::GetRoomMember().lock()) { + // register the network structs to use in slots and signals + 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>(); + 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); + 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* /*old*/, 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 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::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); + } + BringWidgetToFront(lobby); +} + +void MultiplayerState::OnCreateRoom() { + if (host_room == nullptr) { + host_room = new HostRoomWindow(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 you are in a room, leave it + if (auto member = Network::GetRoomMember().lock()) { + member->Leave(); + LOG_DEBUG(Frontend, "Left the room (as a client)"); + } + + // if you are hosting a room, also stop hosting + if (room->GetState() != Network::Room::State::Open) { + return true; + } + // Save ban list + UISettings::values.ban_list = std::move(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; // Do not show notification if the chat window currently has focus + 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; + } + } + // If the user is not a member of a room, show the lobby instead. + // This is currently only used on the clickable label in the status bar + OnViewLobby(); +} + +void MultiplayerState::OnDirectConnectToRoom() { + if (direct_connect == nullptr) { + direct_connect = new DirectConnectWindow(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); + } +} |