state.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <QAction>
  4. #include <QApplication>
  5. #include <QIcon>
  6. #include <QMessageBox>
  7. #include <QStandardItemModel>
  8. #include "common/announce_multiplayer_room.h"
  9. #include "common/logging/log.h"
  10. #include "yuzu/game_list.h"
  11. #include "yuzu/multiplayer/client_room.h"
  12. #include "yuzu/multiplayer/direct_connect.h"
  13. #include "yuzu/multiplayer/host_room.h"
  14. #include "yuzu/multiplayer/lobby.h"
  15. #include "yuzu/multiplayer/message.h"
  16. #include "yuzu/multiplayer/state.h"
  17. #include "yuzu/uisettings.h"
  18. #include "yuzu/util/clickable_label.h"
  19. MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_,
  20. QAction* leave_room_, QAction* show_room_,
  21. Network::RoomNetwork& room_network_)
  22. : QWidget(parent), game_list_model(game_list_model_), leave_room(leave_room_),
  23. show_room(show_room_), room_network{room_network_} {
  24. if (auto member = room_network.GetRoomMember().lock()) {
  25. // register the network structs to use in slots and signals
  26. state_callback_handle = member->BindOnStateChanged(
  27. [this](const Network::RoomMember::State& state) { emit NetworkStateChanged(state); });
  28. connect(this, &MultiplayerState::NetworkStateChanged, this,
  29. &MultiplayerState::OnNetworkStateChanged);
  30. error_callback_handle = member->BindOnError(
  31. [this](const Network::RoomMember::Error& error) { emit NetworkError(error); });
  32. connect(this, &MultiplayerState::NetworkError, this, &MultiplayerState::OnNetworkError);
  33. }
  34. qRegisterMetaType<Network::RoomMember::State>();
  35. qRegisterMetaType<Network::RoomMember::Error>();
  36. qRegisterMetaType<WebService::WebResult>();
  37. announce_multiplayer_session = std::make_shared<Core::AnnounceMultiplayerSession>(room_network);
  38. announce_multiplayer_session->BindErrorCallback(
  39. [this](const WebService::WebResult& result) { emit AnnounceFailed(result); });
  40. connect(this, &MultiplayerState::AnnounceFailed, this, &MultiplayerState::OnAnnounceFailed);
  41. status_text = new ClickableLabel(this);
  42. status_icon = new ClickableLabel(this);
  43. status_text->setToolTip(tr("Current connection status"));
  44. status_text->setText(tr("Not Connected. Click here to find a room!"));
  45. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
  46. connect(status_text, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
  47. connect(status_icon, &ClickableLabel::clicked, this, &MultiplayerState::OnOpenNetworkRoom);
  48. connect(static_cast<QApplication*>(QApplication::instance()), &QApplication::focusChanged, this,
  49. [this](QWidget* /*old*/, QWidget* now) {
  50. if (client_room && client_room->isAncestorOf(now)) {
  51. HideNotification();
  52. }
  53. });
  54. }
  55. MultiplayerState::~MultiplayerState() = default;
  56. void MultiplayerState::Close() {
  57. if (state_callback_handle) {
  58. if (auto member = room_network.GetRoomMember().lock()) {
  59. member->Unbind(state_callback_handle);
  60. }
  61. }
  62. if (error_callback_handle) {
  63. if (auto member = room_network.GetRoomMember().lock()) {
  64. member->Unbind(error_callback_handle);
  65. }
  66. }
  67. if (host_room) {
  68. host_room->close();
  69. }
  70. if (direct_connect) {
  71. direct_connect->close();
  72. }
  73. if (client_room) {
  74. client_room->close();
  75. }
  76. if (lobby) {
  77. lobby->close();
  78. }
  79. }
  80. void MultiplayerState::retranslateUi() {
  81. status_text->setToolTip(tr("Current connection status"));
  82. if (current_state == Network::RoomMember::State::Uninitialized) {
  83. status_text->setText(tr("Not Connected. Click here to find a room!"));
  84. } else if (current_state == Network::RoomMember::State::Joined ||
  85. current_state == Network::RoomMember::State::Moderator) {
  86. status_text->setText(tr("Connected"));
  87. } else {
  88. status_text->setText(tr("Not Connected"));
  89. }
  90. if (lobby) {
  91. lobby->RetranslateUi();
  92. }
  93. if (host_room) {
  94. host_room->RetranslateUi();
  95. }
  96. if (client_room) {
  97. client_room->RetranslateUi();
  98. }
  99. if (direct_connect) {
  100. direct_connect->RetranslateUi();
  101. }
  102. }
  103. void MultiplayerState::OnNetworkStateChanged(const Network::RoomMember::State& state) {
  104. LOG_DEBUG(Frontend, "Network State: {}", Network::GetStateStr(state));
  105. if (state == Network::RoomMember::State::Joined ||
  106. state == Network::RoomMember::State::Moderator) {
  107. OnOpenNetworkRoom();
  108. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
  109. status_text->setText(tr("Connected"));
  110. leave_room->setEnabled(true);
  111. show_room->setEnabled(true);
  112. } else {
  113. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
  114. status_text->setText(tr("Not Connected"));
  115. leave_room->setEnabled(false);
  116. show_room->setEnabled(false);
  117. }
  118. current_state = state;
  119. }
  120. void MultiplayerState::OnNetworkError(const Network::RoomMember::Error& error) {
  121. LOG_DEBUG(Frontend, "Network Error: {}", Network::GetErrorStr(error));
  122. switch (error) {
  123. case Network::RoomMember::Error::LostConnection:
  124. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::LOST_CONNECTION);
  125. break;
  126. case Network::RoomMember::Error::HostKicked:
  127. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::HOST_KICKED);
  128. break;
  129. case Network::RoomMember::Error::CouldNotConnect:
  130. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::UNABLE_TO_CONNECT);
  131. break;
  132. case Network::RoomMember::Error::NameCollision:
  133. NetworkMessage::ErrorManager::ShowError(
  134. NetworkMessage::ErrorManager::USERNAME_NOT_VALID_SERVER);
  135. break;
  136. case Network::RoomMember::Error::IpCollision:
  137. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::IP_COLLISION);
  138. break;
  139. case Network::RoomMember::Error::RoomIsFull:
  140. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::ROOM_IS_FULL);
  141. break;
  142. case Network::RoomMember::Error::WrongPassword:
  143. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::WRONG_PASSWORD);
  144. break;
  145. case Network::RoomMember::Error::WrongVersion:
  146. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::WRONG_VERSION);
  147. break;
  148. case Network::RoomMember::Error::HostBanned:
  149. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::HOST_BANNED);
  150. break;
  151. case Network::RoomMember::Error::UnknownError:
  152. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::UNABLE_TO_CONNECT);
  153. break;
  154. case Network::RoomMember::Error::PermissionDenied:
  155. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::PERMISSION_DENIED);
  156. break;
  157. case Network::RoomMember::Error::NoSuchUser:
  158. NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::NO_SUCH_USER);
  159. break;
  160. }
  161. }
  162. void MultiplayerState::OnAnnounceFailed(const WebService::WebResult& result) {
  163. announce_multiplayer_session->Stop();
  164. QMessageBox::warning(this, tr("Error"),
  165. tr("Failed to update the room information. Please check your Internet "
  166. "connection and try hosting the room again.\nDebug Message: ") +
  167. QString::fromStdString(result.result_string),
  168. QMessageBox::Ok);
  169. }
  170. void MultiplayerState::UpdateThemedIcons() {
  171. if (show_notification) {
  172. status_icon->setPixmap(
  173. QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16));
  174. } else if (current_state == Network::RoomMember::State::Joined ||
  175. current_state == Network::RoomMember::State::Moderator) {
  176. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
  177. } else {
  178. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("disconnected")).pixmap(16));
  179. }
  180. if (client_room)
  181. client_room->UpdateIconDisplay();
  182. }
  183. static void BringWidgetToFront(QWidget* widget) {
  184. widget->show();
  185. widget->activateWindow();
  186. widget->raise();
  187. }
  188. void MultiplayerState::OnViewLobby() {
  189. if (lobby == nullptr) {
  190. lobby = new Lobby(this, game_list_model, announce_multiplayer_session, room_network);
  191. }
  192. BringWidgetToFront(lobby);
  193. }
  194. void MultiplayerState::OnCreateRoom() {
  195. if (host_room == nullptr) {
  196. host_room =
  197. new HostRoomWindow(this, game_list_model, announce_multiplayer_session, room_network);
  198. }
  199. BringWidgetToFront(host_room);
  200. }
  201. bool MultiplayerState::OnCloseRoom() {
  202. if (!NetworkMessage::WarnCloseRoom())
  203. return false;
  204. if (auto room = room_network.GetRoom().lock()) {
  205. // if you are in a room, leave it
  206. if (auto member = room_network.GetRoomMember().lock()) {
  207. member->Leave();
  208. LOG_DEBUG(Frontend, "Left the room (as a client)");
  209. }
  210. // if you are hosting a room, also stop hosting
  211. if (room->GetState() != Network::Room::State::Open) {
  212. return true;
  213. }
  214. // Save ban list
  215. UISettings::values.multiplayer_ban_list = std::move(room->GetBanList());
  216. room->Destroy();
  217. announce_multiplayer_session->Stop();
  218. LOG_DEBUG(Frontend, "Closed the room (as a server)");
  219. }
  220. return true;
  221. }
  222. void MultiplayerState::ShowNotification() {
  223. if (client_room && client_room->isAncestorOf(QApplication::focusWidget()))
  224. return; // Do not show notification if the chat window currently has focus
  225. show_notification = true;
  226. QApplication::alert(nullptr);
  227. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected_notification")).pixmap(16));
  228. status_text->setText(tr("New Messages Received"));
  229. }
  230. void MultiplayerState::HideNotification() {
  231. show_notification = false;
  232. status_icon->setPixmap(QIcon::fromTheme(QStringLiteral("connected")).pixmap(16));
  233. status_text->setText(tr("Connected"));
  234. }
  235. void MultiplayerState::OnOpenNetworkRoom() {
  236. if (auto member = room_network.GetRoomMember().lock()) {
  237. if (member->IsConnected()) {
  238. if (client_room == nullptr) {
  239. client_room = new ClientRoomWindow(this, room_network);
  240. connect(client_room, &ClientRoomWindow::ShowNotification, this,
  241. &MultiplayerState::ShowNotification);
  242. }
  243. BringWidgetToFront(client_room);
  244. return;
  245. }
  246. }
  247. // If the user is not a member of a room, show the lobby instead.
  248. // This is currently only used on the clickable label in the status bar
  249. OnViewLobby();
  250. }
  251. void MultiplayerState::OnDirectConnectToRoom() {
  252. if (direct_connect == nullptr) {
  253. direct_connect = new DirectConnectWindow(room_network, this);
  254. }
  255. BringWidgetToFront(direct_connect);
  256. }
  257. bool MultiplayerState::IsHostingPublicRoom() const {
  258. return announce_multiplayer_session->IsRunning();
  259. }
  260. void MultiplayerState::UpdateCredentials() {
  261. announce_multiplayer_session->UpdateCredentials();
  262. }
  263. void MultiplayerState::UpdateGameList(QStandardItemModel* game_list) {
  264. game_list_model = game_list;
  265. if (lobby) {
  266. lobby->UpdateGameList(game_list);
  267. }
  268. if (host_room) {
  269. host_room->UpdateGameList(game_list);
  270. }
  271. }