From 550b08335e22bd27c06867ad8e3a82dacfddc33e Mon Sep 17 00:00:00 2001 From: Collecting Date: Thu, 22 Jan 2026 05:30:49 +0100 Subject: [PATCH 1/4] fix(multiplayer): Remove Socket Re-utilization Signed-off-by: Collecting --- src/core/hle/service/sockets/bsd.cpp | 45 +++------------------------- 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp index 7ae1cde4b..6912ead04 100644 --- a/src/core/hle/service/sockets/bsd.cpp +++ b/src/core/hle/service/sockets/bsd.cpp @@ -553,25 +553,10 @@ std::pair BSD::SocketImpl(Domain domain, Type type, Protocol protoco descriptor.protocol = Translate(protocol); descriptor.is_connection_based = IsConnectionBased(type); - // Try to reuse a socket from the pool if using proxy if (using_proxy) { - SocketPoolKey key{descriptor.domain, descriptor.type, descriptor.protocol}; - std::lock_guard lock(socket_pool_mutex); - - auto it = socket_pool.find(key); - if (it != socket_pool.end() && !it->second.empty()) { - descriptor.socket = it->second.back(); - it->second.pop_back(); - - // call Initialize here so socket_proxy.cpp functions work - descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol); - - LOG_DEBUG(Service, "Reused socket from pool for fd={}", fd); - } else { - descriptor.socket = std::make_shared(room_network); - descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol); - LOG_DEBUG(Service, "Created new ProxySocket for fd={}", fd); - } + descriptor.socket = std::make_shared(room_network); + descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol); + LOG_DEBUG(Service, "Created new ProxySocket for fd={}", fd); } else { descriptor.socket = std::make_shared(); descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol); @@ -1008,39 +993,17 @@ Errno BSD::CloseImpl(s32 fd) { return Errno::BADF; } - // Capture the socket pointer and info before we lock and reset the table entry std::shared_ptr socket_to_close; - SocketPoolKey key; - bool is_proxy = false; { std::lock_guard lock(fd_table_mutex); - auto& descriptor = file_descriptors[fd]; - socket_to_close = descriptor->socket; - key = {descriptor->domain, descriptor->type, descriptor->protocol}; - is_proxy = std::dynamic_pointer_cast(socket_to_close) != nullptr; - - // Remove it from the table immediately so OnProxyPacketReceived stops seeing it + socket_to_close = file_descriptors[fd]->socket; file_descriptors[fd].reset(); } const Errno bsd_errno = Translate(socket_to_close->Close()); - if (bsd_errno != Errno::SUCCESS) { - return bsd_errno; - } - LOG_INFO(Service, "Close socket fd={}", fd); - auto room_member = room_network.GetRoomMember().lock(); - if (is_proxy && room_member && room_member->IsConnected()) { - std::lock_guard lock(socket_pool_mutex); - constexpr size_t MAX_POOL_SIZE = 8; - if (socket_pool[key].size() < MAX_POOL_SIZE) { - socket_pool[key].push_back(socket_to_close); - LOG_DEBUG(Service, "Returned socket fd={} to pool", fd); - } - } - return bsd_errno; } From cc85173fa942c088abaa65a70f7886b04e5cca2b Mon Sep 17 00:00:00 2001 From: Collecting Date: Thu, 22 Jan 2026 05:31:36 +0100 Subject: [PATCH 2/4] fix(multiplayer): Add sleep_for & remove yield for Stabilization Signed-off-by: Collecting --- src/core/internal_network/socket_proxy.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index b6368e4b5..4d2e706e2 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -34,14 +34,13 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { const auto my_ip = room_member->GetFakeIpAddress(); - // If the sender (local_endpoint) is OUR IP, ignore it. - // We don't want to process our own sent packets. + // 1. Ignore our own echo if (packet.local_endpoint.ip == my_ip) { return; } - // Only accept packets meant for us or actual broadcasts. - if (packet.remote_endpoint.ip != my_ip && !packet.broadcast) { + // 2. Ignore packets meant for other people + if (!packet.broadcast && packet.remote_endpoint.ip != my_ip) { return; } @@ -173,7 +172,7 @@ std::pair ProxySocket::RecvFrom(int flags, std::span message, So return {-1, Errno::AGAIN}; } - std::this_thread::yield(); + std::this_thread::sleep_for(std::chrono::milliseconds(1)); const auto time_diff = std::chrono::steady_clock::now() - timestamp; const auto time_diff_ms = From 2b810e766afcaf68990160b927ce503c20bd489b Mon Sep 17 00:00:00 2001 From: Collecting Date: Thu, 22 Jan 2026 05:32:14 +0100 Subject: [PATCH 3/4] fix(multiplayer): Add mutable to packet_mutex Signed-off-by: Collecting --- src/core/hle/service/ldn/lan_discovery.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/hle/service/ldn/lan_discovery.h b/src/core/hle/service/ldn/lan_discovery.h index 855f58661..213602ad2 100644 --- a/src/core/hle/service/ldn/lan_discovery.h +++ b/src/core/hle/service/ldn/lan_discovery.h @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -112,7 +113,7 @@ protected: static constexpr Ssid fake_ssid{"CitronFakeSsidForLdn"}; bool inited{}; - std::mutex packet_mutex; + mutable std::mutex packet_mutex; std::array stations; std::array node_changes{}; std::array node_last_states{}; From 44e8e2a630af0387b50fc455dab0c2755691ce95 Mon Sep 17 00:00:00 2001 From: Collecting Date: Thu, 22 Jan 2026 05:32:45 +0100 Subject: [PATCH 4/4] fix(multiplayer): Ensure Node = 1 and not 0 Signed-off-by: Collecting --- src/core/hle/service/ldn/lan_discovery.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/core/hle/service/ldn/lan_discovery.cpp b/src/core/hle/service/ldn/lan_discovery.cpp index 237f4ba0e..96790b109 100644 --- a/src/core/hle/service/ldn/lan_discovery.cpp +++ b/src/core/hle/service/ldn/lan_discovery.cpp @@ -77,16 +77,17 @@ void LANDiscovery::SetState(State new_state) { } Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const { + std::scoped_lock lock{packet_mutex}; if (state == State::AccessPointCreated || state == State::StationConnected) { std::memcpy(&out_network, &network_info, sizeof(network_info)); return ResultSuccess; } - return ResultBadState; } Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network, std::span out_updates) { + std::scoped_lock lock{packet_mutex}; if (out_updates.size() > NodeCountMax) { return ResultInvalidBufferCount; } @@ -376,7 +377,9 @@ void LANDiscovery::UpdateNodes() { } station.OverrideInfo(); } - network_info.ldn.node_count = count + 1; + + // Node Count Guard: Ensure we always report at least 1 node + network_info.ldn.node_count = std::max(1, count + 1); for (auto local_ip : connected_clients) { SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip); @@ -387,6 +390,7 @@ void LANDiscovery::UpdateNodes() { void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) { network_info = info; + if (state == State::StationOpened) { SetState(State::StationConnected); } @@ -567,10 +571,20 @@ void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) { } case Network::LDNPacketType::SyncNetwork: { if (state == State::StationOpened || state == State::StationConnected) { - LOG_INFO(Frontend, "SyncNetwork packet received!"); + if (packet.data.size() < sizeof(NetworkInfo)) { + break; + } + NetworkInfo info{}; std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo)); + if (state == State::StationConnected) { + if (std::memcmp(&info.network_id.session_id, &network_info.network_id.session_id, sizeof(SessionId)) != 0) { + break; + } + } + + LOG_INFO(Frontend, "SyncNetwork packet received!"); OnSyncNetwork(info); } else { LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");