From 02e24d02115f76d6b4be9356fe83b545edfa785f Mon Sep 17 00:00:00 2001 From: Smile Yik Date: Tue, 8 Nov 2022 14:03:25 +0800 Subject: [PATCH] network: play together in different room change with suggested change --- src/dedicated_room/yuzu_room.cpp | 59 ++++++++- src/network/.room_post_office.h.kate-swp | Bin 0 -> 68 bytes src/network/CMakeLists.txt | 2 + src/network/network.cpp | 12 ++ src/network/network.h | 9 +- src/network/room.cpp | 110 +++++++++++++++- src/network/room.h | 10 ++ src/network/room_post_office.cpp | 157 +++++++++++++++++++++++ src/network/room_post_office.h | 40 ++++++ 9 files changed, 394 insertions(+), 5 deletions(-) create mode 100644 src/network/.room_post_office.h.kate-swp create mode 100644 src/network/room_post_office.cpp create mode 100644 src/network/room_post_office.h diff --git a/src/dedicated_room/yuzu_room.cpp b/src/dedicated_room/yuzu_room.cpp index 359891883c..15ac50a86c 100644 --- a/src/dedicated_room/yuzu_room.cpp +++ b/src/dedicated_room/yuzu_room.cpp @@ -61,7 +61,10 @@ static void PrintHelp(const char* argv0) { "--log-file The file for storing the room log\n" "--enable-yuzu-mods Allow yuzu Community Moderators to moderate on your room\n" "-h, --help Display this help and exit\n" - "-v, --version Output version information and exit\n", + "-v, --version Output version information and exit\n" + "--room-post-office Open server as a room post office\n" + "--room-post-host Room post office host\n" + "--room-post-port Room post office port\n", argv0); } @@ -180,6 +183,34 @@ static void InitializeLogging(const std::string& log_file) { Common::Log::Start(); } +static int RunRoomPostOffice(Common::DetachedTasks& detached_tasks, u32 port) { + if (port > UINT16_MAX) { + LOG_ERROR(Network, "Port needs to be in the range 0 - 65535!"); + return -1; + } + + Network::RoomNetwork network{}; + network.Init(); + if (auto post_office = network.GetRoomPostOffice().lock()) { + if (!post_office->Create("", port)) { + LOG_INFO(Network, "Failed to create room post office: "); + return -1; + } + LOG_INFO(Network, "Room post office is open. Close with Q+Enter..."); + while (true) { + std::string in; + std::cin >> in; + if (in.size() > 0) { + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + } + network.Shutdown(); + detached_tasks.WaitForAllTasks(); + return 0; +} + /// Application entry point int main(int argc, char** argv) { Common::DetachedTasks detached_tasks; @@ -195,10 +226,13 @@ int main(int argc, char** argv) { std::string web_api_url; std::string ban_list_file; std::string log_file = "yuzu-room.log"; + std::string room_post_host = "127.0.0.1"; u64 preferred_game_id = 0; u32 port = Network::DefaultRoomPort; + u32 room_post_port = Network::DefaultRoomPort; u32 max_members = 16; bool enable_yuzu_mods = false; + bool room_post_office = false; static struct option long_options[] = { {"room-name", required_argument, 0, 'n'}, @@ -216,6 +250,9 @@ int main(int argc, char** argv) { {"enable-yuzu-mods", no_argument, 0, 'e'}, {"help", no_argument, 0, 'h'}, {"version", no_argument, 0, 'v'}, + {"room-post-office", no_argument, 0, 'c'}, + {"room-post-host", required_argument, 0, 'f'}, + {"room-post-port", required_argument, 0, 'j'}, {0, 0, 0, 0}, }; @@ -270,10 +307,23 @@ int main(int argc, char** argv) { case 'v': PrintVersion(); return 0; + case 'c': + room_post_office = true; + break; + case 'f': + room_post_host.assign(optarg); + break; + case 'j': + room_post_port = strtoul(optarg, &endarg, 0); + break; } } } + if (room_post_office) { + return RunRoomPostOffice(detached_tasks, room_post_port); + } + if (room_name.empty()) { LOG_ERROR(Network, "Room name is empty!"); PrintHelp(argv[0]); @@ -353,6 +403,10 @@ int main(int argc, char** argv) { verify_backend = std::make_unique(); } + if (!room_post_host.empty()) { + room_post_office = true; + } + Network::RoomNetwork network{}; network.Init(); if (auto room = network.GetRoom().lock()) { @@ -369,6 +423,9 @@ int main(int argc, char** argv) { if (announce) { announce_session->Start(); } + if (room_post_office && !room->HasMailBox()) { + room->SetupMailBox(room_post_host, room_post_port); + } while (room->GetState() == Network::Room::State::Open) { std::string in; std::cin >> in; diff --git a/src/network/.room_post_office.h.kate-swp b/src/network/.room_post_office.h.kate-swp new file mode 100644 index 0000000000000000000000000000000000000000..0ed673bb9b034ffc2285e2590cc66749b39a4c3e GIT binary patch literal 68 zcmZQzU=Z?7EJ;-eE>A2_aLdd|RWQ;sU|?VnF`Q`OW_72hJEF9H(); m_room_member = std::make_shared(); + m_room_post_office = std::make_shared(); } bool RoomNetwork::Init() { @@ -20,10 +21,15 @@ bool RoomNetwork::Init() { } m_room = std::make_shared(); m_room_member = std::make_shared(); + m_room_post_office = std::make_shared(); LOG_DEBUG(Network, "initialized OK"); return true; } +std::weak_ptr RoomNetwork::GetRoomPostOffice() { + return m_room_post_office; +} + std::weak_ptr RoomNetwork::GetRoom() { return m_room; } @@ -43,6 +49,12 @@ void RoomNetwork::Shutdown() { m_room->Destroy(); m_room.reset(); } + if (m_room_post_office) { + if (m_room_post_office->GetState() == RoomPostOffice::State::Open) { + m_room_post_office->Destroy(); + } + m_room_post_office.reset(); + } enet_deinitialize(); LOG_DEBUG(Network, "shutdown OK"); } diff --git a/src/network/network.h b/src/network/network.h index e4de207b27..6ddb176db8 100644 --- a/src/network/network.h +++ b/src/network/network.h @@ -6,6 +6,7 @@ #include #include "network/room.h" #include "network/room_member.h" +#include "network/room_post_office.h" namespace Network { @@ -16,6 +17,9 @@ public: /// Initializes and registers the network device, the room, and the room member. bool Init(); + /// Returns a pointer to the room post office handle + std::weak_ptr GetRoomPostOffice(); + /// Returns a pointer to the room handle std::weak_ptr GetRoom(); @@ -26,8 +30,9 @@ public: void Shutdown(); private: - std::shared_ptr m_room_member; ///< RoomMember (Client) for network games - std::shared_ptr m_room; ///< Room (Server) for network games + std::shared_ptr m_room_member; ///< RoomMember (Client) for network games + std::shared_ptr m_room; ///< Room (Server) for network games + std::shared_ptr m_room_post_office; ///< Room (Relay Server) for rooms }; } // namespace Network diff --git a/src/network/room.cpp b/src/network/room.cpp index dc5dbce7fa..0c2e7c5e33 100644 --- a/src/network/room.cpp +++ b/src/network/room.cpp @@ -48,6 +48,15 @@ public: IPBanList ip_ban_list; ///< List of banned IP addresses mutable std::mutex ban_list_mutex; ///< Mutex for the ban lists + struct PostOffice { + static constexpr u8 WaitPacket = 10; ///< Wait post office's reply max times. + ENetPeer* peer = nullptr; ///< post office's peer + std::string address; ///< post office's address. Will skip if this is empty. + u16 port; ///< post office's port + u8 wait = 0; ///< Wait post office's reply times. + }; + PostOffice post_office; + RoomImpl() : random_gen(std::random_device()()) {} /// Thread that receives and dispatches network packets @@ -234,6 +243,26 @@ public: * to all other clients. */ void HandleClientDisconnection(ENetPeer* client); + + /** + * Post recived packet to room post office. + */ + void HandlePostMail(const ENetEvent* event); + + /** + * Check connection between this and room post office. + */ + void HandlePostOfficeConnection(const ENetEvent* event); + + /** + * Connect to room post office. + */ + bool ConnectPostOffice(); + + /** + * Disconnect room post office + */ + void DisconnectPostOffice(); }; // RoomImpl @@ -251,9 +280,11 @@ void Room::RoomImpl::ServerLoop() { HandleGameInfoPacket(&event); break; case IdProxyPacket: + HandlePostMail(&event); HandleProxyPacket(&event); break; case IdLdnPacket: + HandlePostMail(&event); HandleLdnPacket(&event); break; case IdChatMessage: @@ -283,9 +314,11 @@ void Room::RoomImpl::ServerLoop() { break; } } + HandlePostOfficeConnection(&event); } // Close the connection to all members: SendCloseMessage(); + DisconnectPostOffice(); } void Room::RoomImpl::StartLoop() { @@ -859,7 +892,7 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) { if (member != members.end()) { enet_peer_send(member->peer, 0, enet_packet); } else { - LOG_ERROR(Network, + LOG_DEBUG(Network, "Attempting to send to unknown IP address: " "{}.{}.{}.{}", destination_address[0], destination_address[1], destination_address[2], @@ -913,7 +946,7 @@ void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) { if (member != members.end()) { enet_peer_send(member->peer, 0, enet_packet); } else { - LOG_ERROR(Network, + LOG_DEBUG(Network, "Attempting to send to unknown IP address: " "{}.{}.{}.{}", destination_address[0], destination_address[1], destination_address[2], @@ -1037,6 +1070,68 @@ void Room::RoomImpl::HandleClientDisconnection(ENetPeer* client) { BroadcastRoomInformation(); } +void Room::RoomImpl::HandlePostMail(const ENetEvent* event) { + if (!post_office.peer) { + ConnectPostOffice(); + } + Packet out_packet; + out_packet.Append(event->packet->data, event->packet->dataLength); + ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(), + ENET_PACKET_FLAG_RELIABLE); + if (post_office.peer && event->peer != post_office.peer) { + enet_peer_send(post_office.peer, 0, enet_packet); + } +} + +bool Room::RoomImpl::ConnectPostOffice() { + if (post_office.address.empty()) { + return false; + } + + LOG_INFO(Network, "Try to Connecting room post office {}:{} ...", post_office.address, + post_office.port); + + ENetAddress address{}; + enet_address_set_host(&address, post_office.address.c_str()); + address.port = post_office.port; + post_office.peer = enet_host_connect(server, &address, NumChannels, 0); + if (!post_office.peer) { + LOG_ERROR(Network, "Connect to room post office {}:{} failed!", post_office.address, + post_office.port); + return false; + } + post_office.wait = post_office.WaitPacket - 1; + + return true; +} + +void Room::RoomImpl::DisconnectPostOffice() { + post_office.address.clear(); + if (post_office.peer) { + enet_peer_disconnect(post_office.peer, 0); + post_office.peer = nullptr; + } +} + +void Room::RoomImpl::HandlePostOfficeConnection(const ENetEvent* event) { + if (post_office.address.empty() || post_office.wait == post_office.WaitPacket) { + return; + } else if (event->peer == post_office.peer) { + post_office.wait = post_office.WaitPacket; + LOG_INFO(Network, "Connect to room post office {}:{} success!", post_office.address, + post_office.port); + return; + } + --post_office.wait; + if (post_office.wait == 0) { + post_office.wait = 0xFF; + LOG_ERROR(Network, "Room post office {}:{} have not response!", post_office.address, + post_office.port); + } else if (post_office.wait == post_office.WaitPacket + 1) { + ConnectPostOffice(); + } +} + // Room Room::Room() : room_impl{std::make_unique()} {} @@ -1140,4 +1235,15 @@ void Room::Destroy() { room_impl->room_information.name.clear(); } +bool Room::SetupMailBox(std::string server_address, u16 server_port) { + room_impl->post_office.address = std::move(server_address); + room_impl->post_office.port = server_port; + + return room_impl->ConnectPostOffice(); +} + +bool Room::HasMailBox() const { + return room_impl->post_office.peer; +} + } // namespace Network diff --git a/src/network/room.h b/src/network/room.h index edbd3ecfb2..0cac882175 100644 --- a/src/network/room.h +++ b/src/network/room.h @@ -140,6 +140,16 @@ public: */ void Destroy(); + /** + * Config post office room's address and port, and connect it. + */ + bool SetupMailBox(std::string server_address, u16 server_port); + + /** + * Checks this room should send packets to post office room or not. + */ + bool HasMailBox() const; + private: class RoomImpl; std::unique_ptr room_impl; diff --git a/src/network/room_post_office.cpp b/src/network/room_post_office.cpp new file mode 100644 index 0000000000..e377514cbb --- /dev/null +++ b/src/network/room_post_office.cpp @@ -0,0 +1,157 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include "common/logging/log.h" +#include "network/room.h" +#include "network/room_post_office.h" + +namespace Network { +class RoomPostOffice::RoomPostOfficeImpl { +public: + ENetHost* server = nullptr; + /// Thread that receives and dispatches network packets + std::unique_ptr room_center_thread; + + std::atomic state{State::Closed}; + + class Mailbox { + public: + ENetPeer* peer; ///< the remote room's peer + }; + using MailboxList = std::vector; + /// remote rooms + MailboxList mailboxes; + + RoomPostOfficeImpl() = default; + + /// Thread function that will receive and dispatch messages until the room is destroyed. + void ServerLoop(); + void StartLoop(); + + /** + * Handle client disconnection. + */ + void HandleClientDisconnection(const ENetPeer* peer); + /** + * Handle mail. + */ + void HandleMail(const ENetEvent* event); + /** + * Handle the room joined room post office. + */ + void HandleJoinMailbox(const ENetEvent* event); +}; + +RoomPostOffice::RoomPostOffice() : room_post_office_impl{std::make_unique()} {} +RoomPostOffice::~RoomPostOffice() = default; + +void RoomPostOffice::RoomPostOfficeImpl::ServerLoop() { + while (state != State::Closed) { + ENetEvent event; + if (enet_host_service(server, &event, 5) > 0) { + switch (event.type) { + case ENET_EVENT_TYPE_RECEIVE: + HandleMail(&event); + break; + case ENET_EVENT_TYPE_DISCONNECT: + HandleClientDisconnection(event.peer); + break; + case ENET_EVENT_TYPE_NONE: + break; + case ENET_EVENT_TYPE_CONNECT: + HandleJoinMailbox(&event); + break; + } + } + } + + for (const Mailbox& mailbox : mailboxes) { + if (mailbox.peer) { + enet_peer_disconnect(mailbox.peer, 0); + } + } +} + +bool RoomPostOffice::Create(const std::string& server_address, u16 server_port) { + ENetAddress address; + address.host = ENET_HOST_ANY; + if (!server_address.empty()) { + enet_address_set_host(&address, server_address.c_str()); + } + address.port = server_port; + room_post_office_impl->server = enet_host_create(&address, 16, NumChannels, 0, 0); + if (!room_post_office_impl->server) { + return false; + } + room_post_office_impl->state = State::Open; + room_post_office_impl->StartLoop(); + return true; +} + +bool RoomPostOffice::Destroy() { + room_post_office_impl->state = State::Closed; + room_post_office_impl->room_center_thread->join(); + room_post_office_impl->room_center_thread.reset(); + if (room_post_office_impl->server) { + enet_host_destroy(room_post_office_impl->server); + } + room_post_office_impl->server = nullptr; + room_post_office_impl->mailboxes.clear(); + return true; +} + +void RoomPostOffice::RoomPostOfficeImpl::StartLoop() { + room_center_thread = + std::make_unique(&RoomPostOffice::RoomPostOfficeImpl::ServerLoop, this); +} + +void RoomPostOffice::RoomPostOfficeImpl::HandleClientDisconnection(const ENetPeer* peer) { + if (!peer) { + return; + } + const auto& mailbox = + std::find_if(mailboxes.begin(), mailboxes.end(), + [peer](const Mailbox& mailbox_entry) { return mailbox_entry.peer == peer; }); + if (mailbox != mailboxes.end()) { + mailboxes.erase(mailbox); + LOG_INFO(Network, "some room leave to this room center"); + } +} + +void RoomPostOffice::RoomPostOfficeImpl::HandleMail(const ENetEvent* event) { + for (const auto& mailbox : mailboxes) { + if (mailbox.peer && mailbox.peer != event->peer) { + enet_peer_send(mailbox.peer, 0, event->packet); + } + } + enet_host_flush(server); +} + +void RoomPostOffice::RoomPostOfficeImpl::HandleJoinMailbox(const ENetEvent* event) { + if (!event || !event->peer) { + return; + } + Mailbox mailbox{}; + mailbox.peer = event->peer; + + const auto& result = + std::find_if(mailboxes.begin(), mailboxes.end(), [mailbox](const Mailbox& mailbox_entry) { + return mailbox_entry.peer == mailbox.peer; + }); + if (result == mailboxes.end()) { + mailboxes.push_back(mailbox); + ENetAddress address = event->peer->address; + LOG_INFO(Network, "{}.{}.{}.{}:{} Join to this room post office", (address.host) & 0xFF, + (address.host >> 8) & 0xFF, (address.host >> 16) & 0xFF, + (address.host >> 24) & 0xFF, address.port); + } +} + +RoomPostOffice::State RoomPostOffice::GetState() const { + return room_post_office_impl->state; +} + +} // namespace Network diff --git a/src/network/room_post_office.h b/src/network/room_post_office.h new file mode 100644 index 0000000000..319138463a --- /dev/null +++ b/src/network/room_post_office.h @@ -0,0 +1,40 @@ +// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +namespace Network { +class RoomPostOffice final { +public: + enum class State : u8 { + Open, ///< The room post office is open and ready to accept connections. + Closed, ///< The room post office is not opened and can not accept connections. + }; + + RoomPostOffice(); + ~RoomPostOffice(); + + /** + * Gets the current state of the room post office. + */ + State GetState() const; + + /** + * Creates the socket for this room post office. + * Will bind to default address if server is empty string. + */ + bool Create(const std::string& server_addr, u16 server_port); + + /** + * Destorys the socket. + */ + bool Destroy(); + +private: + class RoomPostOfficeImpl; + std::unique_ptr room_post_office_impl; +}; +} // namespace Network