Merge pull request #8822 from FearlessTobi/multiplayer-fixes
network: Fixes and improvements to the room feature
This commit is contained in:
		| @@ -16,6 +16,7 @@ namespace AnnounceMultiplayerRoom { | ||||
| struct GameInfo { | ||||
|     std::string name{""}; | ||||
|     u64 id{0}; | ||||
|     std::string version{""}; | ||||
| }; | ||||
|  | ||||
| struct Member { | ||||
|   | ||||
| @@ -2,8 +2,6 @@ | ||||
| # SPDX-License-Identifier: GPL-2.0-or-later | ||||
|  | ||||
| add_library(core STATIC | ||||
|     announce_multiplayer_session.cpp | ||||
|     announce_multiplayer_session.h | ||||
|     arm/arm_interface.h | ||||
|     arm/arm_interface.cpp | ||||
|     arm/dynarmic/arm_dynarmic_32.cpp | ||||
|   | ||||
| @@ -319,10 +319,19 @@ struct System::Impl { | ||||
|         if (app_loader->ReadTitle(name) != Loader::ResultStatus::Success) { | ||||
|             LOG_ERROR(Core, "Failed to read title for ROM (Error {})", load_result); | ||||
|         } | ||||
|  | ||||
|         std::string title_version; | ||||
|         const FileSys::PatchManager pm(program_id, system.GetFileSystemController(), | ||||
|                                        system.GetContentProvider()); | ||||
|         const auto metadata = pm.GetControlMetadata(); | ||||
|         if (metadata.first != nullptr) { | ||||
|             title_version = metadata.first->GetVersionString(); | ||||
|         } | ||||
|         if (auto room_member = room_network.GetRoomMember().lock()) { | ||||
|             Network::GameInfo game_info; | ||||
|             game_info.name = name; | ||||
|             game_info.id = program_id; | ||||
|             game_info.version = title_version; | ||||
|             room_member->SendGameInfo(game_info); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -534,7 +534,7 @@ public: | ||||
|  | ||||
| private: | ||||
|     void CheckAvailability(Kernel::HLERequestContext& ctx) { | ||||
|         LOG_WARNING(Service_ACC, "(STUBBED) called"); | ||||
|         LOG_DEBUG(Service_ACC, "(STUBBED) called"); | ||||
|         IPC::ResponseBuilder rb{ctx, 3}; | ||||
|         rb.Push(ResultSuccess); | ||||
|         rb.Push(false); // TODO: Check when this is supposed to return true and when not | ||||
|   | ||||
| @@ -113,7 +113,7 @@ enum class LinkLevel : s8 { | ||||
|     Bad, | ||||
|     Low, | ||||
|     Good, | ||||
|     Excelent, | ||||
|     Excellent, | ||||
| }; | ||||
|  | ||||
| struct NodeLatestUpdate { | ||||
| @@ -145,11 +145,19 @@ struct NetworkId { | ||||
| static_assert(sizeof(NetworkId) == 0x20, "NetworkId is an invalid size"); | ||||
|  | ||||
| struct Ssid { | ||||
|     u8 length; | ||||
|     std::array<char, SsidLengthMax + 1> raw; | ||||
|     u8 length{}; | ||||
|     std::array<char, SsidLengthMax + 1> raw{}; | ||||
|  | ||||
|     Ssid() = default; | ||||
|  | ||||
|     explicit Ssid(std::string_view data) { | ||||
|         length = static_cast<u8>(std::min(data.size(), SsidLengthMax)); | ||||
|         data.copy(raw.data(), length); | ||||
|         raw[length] = 0; | ||||
|     } | ||||
|  | ||||
|     std::string GetStringValue() const { | ||||
|         return std::string(raw.data(), length); | ||||
|         return std::string(raw.data()); | ||||
|     } | ||||
| }; | ||||
| static_assert(sizeof(Ssid) == 0x22, "Ssid is an invalid size"); | ||||
|   | ||||
| @@ -933,7 +933,11 @@ BSD::BSD(Core::System& system_, const char* name) | ||||
|     } | ||||
| } | ||||
|  | ||||
| BSD::~BSD() = default; | ||||
| BSD::~BSD() { | ||||
|     if (auto room_member = room_network.GetRoomMember().lock()) { | ||||
|         room_member->Unbind(proxy_packet_received); | ||||
|     } | ||||
| } | ||||
|  | ||||
| BSDCFG::BSDCFG(Core::System& system_) : ServiceFramework{system_, "bsdcfg"} { | ||||
|     // clang-format off | ||||
|   | ||||
| @@ -26,6 +26,12 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { | ||||
|         closed) { | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (!broadcast && packet.broadcast) { | ||||
|         LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     std::lock_guard guard(packets_mutex); | ||||
|     received_packets.push(packet); | ||||
| } | ||||
| @@ -203,7 +209,7 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, const std::vector<u8>& mess | ||||
|     packet.local_endpoint = local_endpoint; | ||||
|     packet.remote_endpoint = *addr; | ||||
|     packet.protocol = protocol; | ||||
|     packet.broadcast = broadcast; | ||||
|     packet.broadcast = broadcast && packet.remote_endpoint.ip[3] == 255; | ||||
|  | ||||
|     auto& ip = local_endpoint.ip; | ||||
|     auto ipv4 = Network::GetHostIPv4Address(); | ||||
|   | ||||
| @@ -10,7 +10,7 @@ add_executable(yuzu-room | ||||
|  | ||||
| create_target_directory_groups(yuzu-room) | ||||
|  | ||||
| target_link_libraries(yuzu-room PRIVATE common core network) | ||||
| target_link_libraries(yuzu-room PRIVATE common network) | ||||
| if (ENABLE_WEB_SERVICE) | ||||
|     target_compile_definitions(yuzu-room PRIVATE -DENABLE_WEB_SERVICE) | ||||
|     target_link_libraries(yuzu-room PRIVATE web_service) | ||||
|   | ||||
| @@ -27,8 +27,8 @@ | ||||
| #include "common/scm_rev.h" | ||||
| #include "common/settings.h" | ||||
| #include "common/string_util.h" | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "core/core.h" | ||||
| #include "network/announce_multiplayer_session.h" | ||||
| #include "network/network.h" | ||||
| #include "network/room.h" | ||||
| #include "network/verify_user.h" | ||||
| @@ -75,6 +75,12 @@ static constexpr char BanListMagic[] = "YuzuRoom-BanList-1"; | ||||
|  | ||||
| static constexpr char token_delimiter{':'}; | ||||
|  | ||||
| static void PadToken(std::string& token) { | ||||
|     while (token.size() % 4 != 0) { | ||||
|         token.push_back('='); | ||||
|     } | ||||
| } | ||||
|  | ||||
| static std::string UsernameFromDisplayToken(const std::string& display_token) { | ||||
|     std::size_t outlen; | ||||
|  | ||||
| @@ -300,6 +306,7 @@ int main(int argc, char** argv) { | ||||
|         if (username.empty()) { | ||||
|             LOG_INFO(Network, "Hosting a public room"); | ||||
|             Settings::values.web_api_url = web_api_url; | ||||
|             PadToken(token); | ||||
|             Settings::values.yuzu_username = UsernameFromDisplayToken(token); | ||||
|             username = Settings::values.yuzu_username.GetValue(); | ||||
|             Settings::values.yuzu_token = TokenFromDisplayToken(token); | ||||
|   | ||||
| @@ -2,6 +2,8 @@ | ||||
| # SPDX-License-Identifier: GPL-3.0-or-later | ||||
|  | ||||
| add_library(network STATIC | ||||
|     announce_multiplayer_session.cpp | ||||
|     announce_multiplayer_session.h | ||||
|     network.cpp | ||||
|     network.h | ||||
|     packet.cpp | ||||
| @@ -17,3 +19,7 @@ add_library(network STATIC | ||||
| create_target_directory_groups(network) | ||||
|  | ||||
| target_link_libraries(network PRIVATE common enet Boost::boost) | ||||
| if (ENABLE_WEB_SERVICE) | ||||
|     target_compile_definitions(network PRIVATE -DENABLE_WEB_SERVICE) | ||||
|     target_link_libraries(network PRIVATE web_service) | ||||
| endif() | ||||
|   | ||||
| @@ -221,7 +221,7 @@ public: | ||||
|      * Extracts the game name from a received ENet packet and broadcasts it. | ||||
|      * @param event The ENet event that was received. | ||||
|      */ | ||||
|     void HandleGameNamePacket(const ENetEvent* event); | ||||
|     void HandleGameInfoPacket(const ENetEvent* event); | ||||
|  | ||||
|     /** | ||||
|      * Removes the client from the members list if it was in it and announces the change | ||||
| @@ -234,7 +234,7 @@ public: | ||||
| void Room::RoomImpl::ServerLoop() { | ||||
|     while (state != State::Closed) { | ||||
|         ENetEvent event; | ||||
|         if (enet_host_service(server, &event, 50) > 0) { | ||||
|         if (enet_host_service(server, &event, 5) > 0) { | ||||
|             switch (event.type) { | ||||
|             case ENET_EVENT_TYPE_RECEIVE: | ||||
|                 switch (event.packet->data[0]) { | ||||
| @@ -242,7 +242,7 @@ void Room::RoomImpl::ServerLoop() { | ||||
|                     HandleJoinRequest(&event); | ||||
|                     break; | ||||
|                 case IdSetGameInfo: | ||||
|                     HandleGameNamePacket(&event); | ||||
|                     HandleGameInfoPacket(&event); | ||||
|                     break; | ||||
|                 case IdProxyPacket: | ||||
|                     HandleProxyPacket(&event); | ||||
| @@ -778,6 +778,7 @@ void Room::RoomImpl::BroadcastRoomInformation() { | ||||
|             packet.Write(member.fake_ip); | ||||
|             packet.Write(member.game_info.name); | ||||
|             packet.Write(member.game_info.id); | ||||
|             packet.Write(member.game_info.version); | ||||
|             packet.Write(member.user_data.username); | ||||
|             packet.Write(member.user_data.display_name); | ||||
|             packet.Write(member.user_data.avatar_url); | ||||
| @@ -817,6 +818,7 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) { | ||||
|     in_packet.IgnoreBytes(sizeof(u16)); // Port | ||||
|  | ||||
|     in_packet.IgnoreBytes(sizeof(u8)); // Protocol | ||||
|  | ||||
|     bool broadcast; | ||||
|     in_packet.Read(broadcast); // Broadcast | ||||
|  | ||||
| @@ -909,7 +911,7 @@ void Room::RoomImpl::HandleChatPacket(const ENetEvent* event) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { | ||||
| void Room::RoomImpl::HandleGameInfoPacket(const ENetEvent* event) { | ||||
|     Packet in_packet; | ||||
|     in_packet.Append(event->packet->data, event->packet->dataLength); | ||||
|  | ||||
| @@ -917,6 +919,7 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { | ||||
|     GameInfo game_info; | ||||
|     in_packet.Read(game_info.name); | ||||
|     in_packet.Read(game_info.id); | ||||
|     in_packet.Read(game_info.version); | ||||
|  | ||||
|     { | ||||
|         std::lock_guard lock(member_mutex); | ||||
| @@ -935,7 +938,8 @@ void Room::RoomImpl::HandleGameNamePacket(const ENetEvent* event) { | ||||
|             if (game_info.name.empty()) { | ||||
|                 LOG_INFO(Network, "{} is not playing", display_name); | ||||
|             } else { | ||||
|                 LOG_INFO(Network, "{} is playing {}", display_name, game_info.name); | ||||
|                 LOG_INFO(Network, "{} is playing {} ({})", display_name, game_info.name, | ||||
|                          game_info.version); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -159,7 +159,7 @@ void RoomMember::RoomMemberImpl::MemberLoop() { | ||||
|     while (IsConnected()) { | ||||
|         std::lock_guard lock(network_mutex); | ||||
|         ENetEvent event; | ||||
|         if (enet_host_service(client, &event, 100) > 0) { | ||||
|         if (enet_host_service(client, &event, 5) > 0) { | ||||
|             switch (event.type) { | ||||
|             case ENET_EVENT_TYPE_RECEIVE: | ||||
|                 switch (event.packet->data[0]) { | ||||
| @@ -315,6 +315,7 @@ void RoomMember::RoomMemberImpl::HandleRoomInformationPacket(const ENetEvent* ev | ||||
|         packet.Read(member.fake_ip); | ||||
|         packet.Read(member.game_info.name); | ||||
|         packet.Read(member.game_info.id); | ||||
|         packet.Read(member.game_info.version); | ||||
|         packet.Read(member.username); | ||||
|         packet.Read(member.display_name); | ||||
|         packet.Read(member.avatar_url); | ||||
| @@ -622,6 +623,7 @@ void RoomMember::SendGameInfo(const GameInfo& game_info) { | ||||
|     packet.Write(static_cast<u8>(IdSetGameInfo)); | ||||
|     packet.Write(game_info.name); | ||||
|     packet.Write(game_info.id); | ||||
|     packet.Write(game_info.version); | ||||
|     room_member_impl->Send(std::move(packet)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -146,7 +146,7 @@ public: | ||||
|               const std::string& password = "", const std::string& token = ""); | ||||
|  | ||||
|     /** | ||||
|      * Sends a WiFi packet to the room. | ||||
|      * Sends a Proxy packet to the room. | ||||
|      * @param packet The WiFi packet to send. | ||||
|      */ | ||||
|     void SendProxyPacket(const ProxyPacket& packet); | ||||
|   | ||||
| @@ -860,7 +860,7 @@ void GMainWindow::InitializeWidgets() { | ||||
|     }); | ||||
|  | ||||
|     multiplayer_state = new MultiplayerState(this, game_list->GetModel(), ui->action_Leave_Room, | ||||
|                                              ui->action_Show_Room, system->GetRoomNetwork()); | ||||
|                                              ui->action_Show_Room, *system); | ||||
|     multiplayer_state->setVisible(false); | ||||
|  | ||||
|     // Create status bar | ||||
|   | ||||
| @@ -16,7 +16,7 @@ | ||||
| #include <QUrl> | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include "common/logging/log.h" | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "network/announce_multiplayer_session.h" | ||||
| #include "ui_chat_room.h" | ||||
| #include "yuzu/game_list_p.h" | ||||
| #include "yuzu/multiplayer/chat_room.h" | ||||
| @@ -122,19 +122,22 @@ public: | ||||
|     static const int UsernameRole = Qt::UserRole + 2; | ||||
|     static const int AvatarUrlRole = Qt::UserRole + 3; | ||||
|     static const int GameNameRole = Qt::UserRole + 4; | ||||
|     static const int GameVersionRole = Qt::UserRole + 5; | ||||
|  | ||||
|     PlayerListItem() = default; | ||||
|     explicit PlayerListItem(const std::string& nickname, const std::string& username, | ||||
|                             const std::string& avatar_url, const std::string& game_name) { | ||||
|                             const std::string& avatar_url, | ||||
|                             const AnnounceMultiplayerRoom::GameInfo& game_info) { | ||||
|         setEditable(false); | ||||
|         setData(QString::fromStdString(nickname), NicknameRole); | ||||
|         setData(QString::fromStdString(username), UsernameRole); | ||||
|         setData(QString::fromStdString(avatar_url), AvatarUrlRole); | ||||
|         if (game_name.empty()) { | ||||
|         if (game_info.name.empty()) { | ||||
|             setData(QObject::tr("Not playing a game"), GameNameRole); | ||||
|         } else { | ||||
|             setData(QString::fromStdString(game_name), GameNameRole); | ||||
|             setData(QString::fromStdString(game_info.name), GameNameRole); | ||||
|         } | ||||
|         setData(QString::fromStdString(game_info.version), GameVersionRole); | ||||
|     } | ||||
|  | ||||
|     QVariant data(int role) const override { | ||||
| @@ -149,7 +152,13 @@ public: | ||||
|         } else { | ||||
|             name = QStringLiteral("%1 (%2)").arg(nickname, username); | ||||
|         } | ||||
|         return QStringLiteral("%1\n      %2").arg(name, data(GameNameRole).toString()); | ||||
|         const QString version = data(GameVersionRole).toString(); | ||||
|         QString version_string; | ||||
|         if (!version.isEmpty()) { | ||||
|             version_string = QStringLiteral("(%1)").arg(version); | ||||
|         } | ||||
|         return QStringLiteral("%1\n      %2 %3") | ||||
|             .arg(name, data(GameNameRole).toString(), version_string); | ||||
|     } | ||||
| }; | ||||
|  | ||||
| @@ -167,6 +176,10 @@ ChatRoom::ChatRoom(QWidget* parent) : QWidget(parent), ui(std::make_unique<Ui::C | ||||
|  | ||||
|     ui->chat_history->document()->setMaximumBlockCount(max_chat_lines); | ||||
|  | ||||
|     auto font = ui->chat_history->font(); | ||||
|     font.setPointSizeF(10); | ||||
|     ui->chat_history->setFont(font); | ||||
|  | ||||
|     // register the network structs to use in slots and signals | ||||
|     qRegisterMetaType<Network::ChatEntry>(); | ||||
|     qRegisterMetaType<Network::StatusMessageEntry>(); | ||||
| @@ -366,7 +379,7 @@ void ChatRoom::SetPlayerList(const Network::RoomMember::MemberList& member_list) | ||||
|         if (member.nickname.empty()) | ||||
|             continue; | ||||
|         QStandardItem* name_item = new PlayerListItem(member.nickname, member.username, | ||||
|                                                       member.avatar_url, member.game_info.name); | ||||
|                                                       member.avatar_url, member.game_info); | ||||
|  | ||||
| #ifdef ENABLE_WEB_SERVICE | ||||
|         if (!icon_cache.count(member.avatar_url) && !member.avatar_url.empty()) { | ||||
|   | ||||
| @@ -10,7 +10,7 @@ | ||||
| #include <QTime> | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include "common/logging/log.h" | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "network/announce_multiplayer_session.h" | ||||
| #include "ui_client_room.h" | ||||
| #include "yuzu/game_list_p.h" | ||||
| #include "yuzu/multiplayer/client_room.h" | ||||
|   | ||||
| @@ -8,6 +8,8 @@ | ||||
| #include <QString> | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "core/internal_network/network_interface.h" | ||||
| #include "network/network.h" | ||||
| #include "ui_direct_connect.h" | ||||
| #include "yuzu/main.h" | ||||
| @@ -20,9 +22,10 @@ | ||||
|  | ||||
| enum class ConnectionType : u8 { TraversalServer, IP }; | ||||
|  | ||||
| DirectConnectWindow::DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent) | ||||
| DirectConnectWindow::DirectConnectWindow(Core::System& system_, QWidget* parent) | ||||
|     : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | ||||
|       ui(std::make_unique<Ui::DirectConnect>()), room_network{room_network_} { | ||||
|       ui(std::make_unique<Ui::DirectConnect>()), system{system_}, room_network{ | ||||
|                                                                       system.GetRoomNetwork()} { | ||||
|  | ||||
|     ui->setupUi(this); | ||||
|  | ||||
| @@ -53,10 +56,20 @@ void DirectConnectWindow::RetranslateUi() { | ||||
| } | ||||
|  | ||||
| void DirectConnectWindow::Connect() { | ||||
|     if (!Network::GetSelectedNetworkInterface()) { | ||||
|         NetworkMessage::ErrorManager::ShowError( | ||||
|             NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | ||||
|         return; | ||||
|     } | ||||
|     if (!ui->nickname->hasAcceptableInput()) { | ||||
|         NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); | ||||
|         return; | ||||
|     } | ||||
|     if (system.IsPoweredOn()) { | ||||
|         if (!NetworkMessage::WarnGameRunning()) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     if (const auto member = room_network.GetRoomMember().lock()) { | ||||
|         // Prevent the user from trying to join a room while they are already joining. | ||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||
|   | ||||
| @@ -12,11 +12,15 @@ namespace Ui { | ||||
| class DirectConnect; | ||||
| } | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| class DirectConnectWindow : public QDialog { | ||||
|     Q_OBJECT | ||||
|  | ||||
| public: | ||||
|     explicit DirectConnectWindow(Network::RoomNetwork& room_network_, QWidget* parent = nullptr); | ||||
|     explicit DirectConnectWindow(Core::System& system_, QWidget* parent = nullptr); | ||||
|     ~DirectConnectWindow(); | ||||
|  | ||||
|     void RetranslateUi(); | ||||
| @@ -39,5 +43,6 @@ private: | ||||
|     QFutureWatcher<void>* watcher; | ||||
|     std::unique_ptr<Ui::DirectConnect> ui; | ||||
|     Validation validation; | ||||
|     Core::System& system; | ||||
|     Network::RoomNetwork& room_network; | ||||
| }; | ||||
|   | ||||
| @@ -12,7 +12,9 @@ | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include "common/logging/log.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "core/core.h" | ||||
| #include "core/internal_network/network_interface.h" | ||||
| #include "network/announce_multiplayer_session.h" | ||||
| #include "ui_host_room.h" | ||||
| #include "yuzu/game_list_p.h" | ||||
| #include "yuzu/main.h" | ||||
| @@ -27,10 +29,11 @@ | ||||
|  | ||||
| HostRoomWindow::HostRoomWindow(QWidget* parent, QStandardItemModel* list, | ||||
|                                std::shared_ptr<Core::AnnounceMultiplayerSession> session, | ||||
|                                Network::RoomNetwork& room_network_) | ||||
|                                Core::System& system_) | ||||
|     : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | ||||
|       ui(std::make_unique<Ui::HostRoom>()), | ||||
|       announce_multiplayer_session(session), room_network{room_network_} { | ||||
|       announce_multiplayer_session(session), system{system_}, room_network{ | ||||
|                                                                   system.GetRoomNetwork()} { | ||||
|     ui->setupUi(this); | ||||
|  | ||||
|     // set up validation for all of the fields | ||||
| @@ -105,6 +108,11 @@ std::unique_ptr<Network::VerifyUser::Backend> HostRoomWindow::CreateVerifyBacken | ||||
| } | ||||
|  | ||||
| void HostRoomWindow::Host() { | ||||
|     if (!Network::GetSelectedNetworkInterface()) { | ||||
|         NetworkMessage::ErrorManager::ShowError( | ||||
|             NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | ||||
|         return; | ||||
|     } | ||||
|     if (!ui->username->hasAcceptableInput()) { | ||||
|         NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::USERNAME_NOT_VALID); | ||||
|         return; | ||||
| @@ -121,6 +129,11 @@ void HostRoomWindow::Host() { | ||||
|         NetworkMessage::ErrorManager::ShowError(NetworkMessage::ErrorManager::GAME_NOT_SELECTED); | ||||
|         return; | ||||
|     } | ||||
|     if (system.IsPoweredOn()) { | ||||
|         if (!NetworkMessage::WarnGameRunning()) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|     if (auto member = room_network.GetRoomMember().lock()) { | ||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||
|             return; | ||||
|   | ||||
| @@ -17,8 +17,9 @@ class HostRoom; | ||||
| } | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| class AnnounceMultiplayerSession; | ||||
| } | ||||
| } // namespace Core | ||||
|  | ||||
| class ConnectionError; | ||||
| class ComboBoxProxyModel; | ||||
| @@ -35,7 +36,7 @@ class HostRoomWindow : public QDialog { | ||||
| public: | ||||
|     explicit HostRoomWindow(QWidget* parent, QStandardItemModel* list, | ||||
|                             std::shared_ptr<Core::AnnounceMultiplayerSession> session, | ||||
|                             Network::RoomNetwork& room_network_); | ||||
|                             Core::System& system_); | ||||
|     ~HostRoomWindow(); | ||||
|  | ||||
|     /** | ||||
| @@ -54,6 +55,7 @@ private: | ||||
|     QStandardItemModel* game_list; | ||||
|     ComboBoxProxyModel* proxy; | ||||
|     Validation validation; | ||||
|     Core::System& system; | ||||
|     Network::RoomNetwork& room_network; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,8 @@ | ||||
| #include <QtConcurrent/QtConcurrentRun> | ||||
| #include "common/logging/log.h" | ||||
| #include "common/settings.h" | ||||
| #include "core/core.h" | ||||
| #include "core/internal_network/network_interface.h" | ||||
| #include "network/network.h" | ||||
| #include "ui_lobby.h" | ||||
| #include "yuzu/game_list_p.h" | ||||
| @@ -22,11 +24,11 @@ | ||||
| #endif | ||||
|  | ||||
| Lobby::Lobby(QWidget* parent, QStandardItemModel* list, | ||||
|              std::shared_ptr<Core::AnnounceMultiplayerSession> session, | ||||
|              Network::RoomNetwork& room_network_) | ||||
|              std::shared_ptr<Core::AnnounceMultiplayerSession> session, Core::System& system_) | ||||
|     : QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint | Qt::WindowSystemMenuHint), | ||||
|       ui(std::make_unique<Ui::Lobby>()), | ||||
|       announce_multiplayer_session(session), room_network{room_network_} { | ||||
|       announce_multiplayer_session(session), system{system_}, room_network{ | ||||
|                                                                   system.GetRoomNetwork()} { | ||||
|     ui->setupUi(this); | ||||
|  | ||||
|     // setup the watcher for background connections | ||||
| @@ -114,6 +116,18 @@ void Lobby::OnExpandRoom(const QModelIndex& index) { | ||||
| } | ||||
|  | ||||
| void Lobby::OnJoinRoom(const QModelIndex& source) { | ||||
|     if (!Network::GetSelectedNetworkInterface()) { | ||||
|         NetworkMessage::ErrorManager::ShowError( | ||||
|             NetworkMessage::ErrorManager::NO_INTERFACE_SELECTED); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if (system.IsPoweredOn()) { | ||||
|         if (!NetworkMessage::WarnGameRunning()) { | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if (const auto member = room_network.GetRoomMember().lock()) { | ||||
|         // Prevent the user from trying to join a room while they are already joining. | ||||
|         if (member->GetState() == Network::RoomMember::State::Joining) { | ||||
|   | ||||
| @@ -9,7 +9,7 @@ | ||||
| #include <QSortFilterProxyModel> | ||||
| #include <QStandardItemModel> | ||||
| #include "common/announce_multiplayer_room.h" | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "network/announce_multiplayer_session.h" | ||||
| #include "network/network.h" | ||||
| #include "yuzu/multiplayer/validation.h" | ||||
|  | ||||
| @@ -20,6 +20,10 @@ class Lobby; | ||||
| class LobbyModel; | ||||
| class LobbyFilterProxyModel; | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Listing of all public games pulled from services. The lobby should be simple enough for users to | ||||
|  * find the game they want to play, and join it. | ||||
| @@ -30,7 +34,7 @@ class Lobby : public QDialog { | ||||
| public: | ||||
|     explicit Lobby(QWidget* parent, QStandardItemModel* list, | ||||
|                    std::shared_ptr<Core::AnnounceMultiplayerSession> session, | ||||
|                    Network::RoomNetwork& room_network_); | ||||
|                    Core::System& system_); | ||||
|     ~Lobby() override; | ||||
|  | ||||
|     /** | ||||
| @@ -94,6 +98,7 @@ private: | ||||
|     std::weak_ptr<Core::AnnounceMultiplayerSession> announce_multiplayer_session; | ||||
|     QFutureWatcher<void>* watcher; | ||||
|     Validation validation; | ||||
|     Core::System& system; | ||||
|     Network::RoomNetwork& room_network; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -49,6 +49,9 @@ const ConnectionError ErrorManager::PERMISSION_DENIED( | ||||
|     QT_TR_NOOP("You do not have enough permission to perform this action.")); | ||||
| const ConnectionError ErrorManager::NO_SUCH_USER(QT_TR_NOOP( | ||||
|     "The user you are trying to kick/ban could not be found.\nThey may have left the room.")); | ||||
| const ConnectionError ErrorManager::NO_INTERFACE_SELECTED( | ||||
|     QT_TR_NOOP("No network interface is selected.\nPlease go to Configure -> System -> Network and " | ||||
|                "make a selection.")); | ||||
|  | ||||
| static bool WarnMessage(const std::string& title, const std::string& text) { | ||||
|     return QMessageBox::Ok == QMessageBox::warning(nullptr, QObject::tr(title.c_str()), | ||||
| @@ -60,6 +63,13 @@ void ErrorManager::ShowError(const ConnectionError& e) { | ||||
|     QMessageBox::critical(nullptr, tr("Error"), tr(e.GetString().c_str())); | ||||
| } | ||||
|  | ||||
| bool WarnGameRunning() { | ||||
|     return WarnMessage( | ||||
|         QT_TR_NOOP("Game already running"), | ||||
|         QT_TR_NOOP("Joining a room when the game is already running is discouraged " | ||||
|                    "and can cause the room feature not to work correctly.\nProceed anyway?")); | ||||
| } | ||||
|  | ||||
| bool WarnCloseRoom() { | ||||
|     return WarnMessage( | ||||
|         QT_TR_NOOP("Leave Room"), | ||||
|   | ||||
| @@ -43,11 +43,20 @@ public: | ||||
|     static const ConnectionError IP_COLLISION; | ||||
|     static const ConnectionError PERMISSION_DENIED; | ||||
|     static const ConnectionError NO_SUCH_USER; | ||||
|     static const ConnectionError NO_INTERFACE_SELECTED; | ||||
|     /** | ||||
|      *  Shows a standard QMessageBox with a error message | ||||
|      */ | ||||
|     static void ShowError(const ConnectionError& e); | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Show a standard QMessageBox with a warning message about joining a room when | ||||
|  * the game is already running | ||||
|  * return true if the user wants to close the network connection | ||||
|  */ | ||||
| bool WarnGameRunning(); | ||||
|  | ||||
| /** | ||||
|  * Show a standard QMessageBox with a warning message about leaving the room | ||||
|  * return true if the user wants to close the network connection | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #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" | ||||
| @@ -19,10 +20,9 @@ | ||||
| #include "yuzu/util/clickable_label.h" | ||||
|  | ||||
| MultiplayerState::MultiplayerState(QWidget* parent, QStandardItemModel* game_list_model_, | ||||
|                                    QAction* leave_room_, QAction* show_room_, | ||||
|                                    Network::RoomNetwork& room_network_) | ||||
|                                    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_), room_network{room_network_} { | ||||
|       show_room(show_room_), system{system_}, room_network{system.GetRoomNetwork()} { | ||||
|     if (auto member = room_network.GetRoomMember().lock()) { | ||||
|         // register the network structs to use in slots and signals | ||||
|         state_callback_handle = member->BindOnStateChanged( | ||||
| @@ -208,15 +208,14 @@ static void BringWidgetToFront(QWidget* widget) { | ||||
|  | ||||
| void MultiplayerState::OnViewLobby() { | ||||
|     if (lobby == nullptr) { | ||||
|         lobby = new Lobby(this, game_list_model, announce_multiplayer_session, room_network); | ||||
|         lobby = new Lobby(this, game_list_model, announce_multiplayer_session, system); | ||||
|     } | ||||
|     BringWidgetToFront(lobby); | ||||
| } | ||||
|  | ||||
| void MultiplayerState::OnCreateRoom() { | ||||
|     if (host_room == nullptr) { | ||||
|         host_room = | ||||
|             new HostRoomWindow(this, game_list_model, announce_multiplayer_session, room_network); | ||||
|         host_room = new HostRoomWindow(this, game_list_model, announce_multiplayer_session, system); | ||||
|     } | ||||
|     BringWidgetToFront(host_room); | ||||
| } | ||||
| @@ -279,7 +278,7 @@ void MultiplayerState::OnOpenNetworkRoom() { | ||||
|  | ||||
| void MultiplayerState::OnDirectConnectToRoom() { | ||||
|     if (direct_connect == nullptr) { | ||||
|         direct_connect = new DirectConnectWindow(room_network, this); | ||||
|         direct_connect = new DirectConnectWindow(system, this); | ||||
|     } | ||||
|     BringWidgetToFront(direct_connect); | ||||
| } | ||||
|   | ||||
| @@ -4,7 +4,7 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <QWidget> | ||||
| #include "core/announce_multiplayer_session.h" | ||||
| #include "network/announce_multiplayer_session.h" | ||||
| #include "network/network.h" | ||||
|  | ||||
| class QStandardItemModel; | ||||
| @@ -14,12 +14,16 @@ class ClientRoomWindow; | ||||
| class DirectConnectWindow; | ||||
| class ClickableLabel; | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| class MultiplayerState : public QWidget { | ||||
|     Q_OBJECT; | ||||
|  | ||||
| public: | ||||
|     explicit MultiplayerState(QWidget* parent, QStandardItemModel* game_list, QAction* leave_room, | ||||
|                               QAction* show_room, Network::RoomNetwork& room_network_); | ||||
|                               QAction* show_room, Core::System& system_); | ||||
|     ~MultiplayerState(); | ||||
|  | ||||
|     /** | ||||
| @@ -86,6 +90,7 @@ private: | ||||
|     Network::RoomMember::CallbackHandle<Network::RoomMember::Error> error_callback_handle; | ||||
|  | ||||
|     bool show_notification = false; | ||||
|     Core::System& system; | ||||
|     Network::RoomNetwork& room_network; | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 bunnei
					bunnei