diff options
| author | cirakg <cirakg@cirakg.xyz> | 2026-11-02 23:40:30 +0100 |
|---|---|---|
| committer | cirakg <cirakg@cirakg.xyz> | 2026-11-02 23:42:50 +0100 |
| commit | 3e10d48548a285172ea7cb1f94c58783e1d97754 (patch) | |
| tree | 203de9fd00993656728c37b3e148eac66f954065 /src/core/internal_network/socket_proxy.cpp | |
| parent | 693529b27ec898b867ad5806a0174f69597decc9 (diff) | |
network: fix room host LAN Play and room socket review issues
The room host and room moderators are in the Moderator state, not
Joined, so the airplane mode override for joined rooms did not apply to
them and LAN Play still returned them to the menu. Accept both states.
Share the room address override between the NIFM profile and IP config
calls so they cannot drift apart.
recv() on a room TCP socket now waited for the receive timeout, because
Recv goes through RecvFrom. Room TCP sockets have no peer, so keep the
previous immediate end-of-stream result when nothing is queued.
Close() now drops queued packets and wakes a blocked recv, which then
fails with EBADF. The socket state that the room thread reads when it
queues a packet is now guarded by the packet mutex.
Assisted-by: Claude Opus 5.5 (investigation and implementation)
Diffstat (limited to 'src/core/internal_network/socket_proxy.cpp')
| -rw-r--r-- | src/core/internal_network/socket_proxy.cpp | 62 |
1 files changed, 44 insertions, 18 deletions
diff --git a/src/core/internal_network/socket_proxy.cpp b/src/core/internal_network/socket_proxy.cpp index f42ed61e21..83eb2ce9d0 100644 --- a/src/core/internal_network/socket_proxy.cpp +++ b/src/core/internal_network/socket_proxy.cpp @@ -31,21 +31,21 @@ ProxySocket::~ProxySocket() { } void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) { - if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || - closed) { - return; - } - - if (!broadcast && packet.broadcast) { - LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); - return; - } - - auto decompressed = packet; - decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); - { + // Runs on the room thread, while the game thread may bind, close or reconfigure the socket std::lock_guard guard(packets_mutex); + if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno || + closed) { + return; + } + + if (!broadcast && packet.broadcast) { + LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode"); + return; + } + + auto decompressed = packet; + decompressed.data = Common::Compression::DecompressDataZSTD(packet.data); received_packets.push(std::move(decompressed)); } packets_cv.notify_one(); @@ -58,7 +58,10 @@ Errno ProxySocket::SetSockOpt(SOCKET fd_, int option, T value) { } Errno ProxySocket::Initialize(Domain domain, Type type, Protocol socket_protocol) { - protocol = socket_protocol; + { + std::lock_guard guard(packets_mutex); + protocol = socket_protocol; + } SetSockOpt(fd, SO_TYPE, type); return Errno::SUCCESS; @@ -85,6 +88,7 @@ std::pair<SockAddrIn, Errno> ProxySocket::GetSockName() { } Errno ProxySocket::Bind(SockAddrIn addr) { + std::lock_guard guard(packets_mutex); if (is_bound) { LOG_WARNING(Network, "Rebinding Socket is unimplemented!"); return Errno::SUCCESS; @@ -106,6 +110,14 @@ Errno ProxySocket::Shutdown(ShutdownHow how) { } std::pair<s32, Errno> ProxySocket::Recv(int flags, std::span<u8> message) { + if (protocol == Protocol::TCP) { + // Room TCP sockets have no peer (Connect and Send are stubs). Report an empty queue as the + // end of the stream, as before, instead of blocking the bsd service until the timeout. + std::lock_guard guard(packets_mutex); + if (received_packets.empty()) { + return {0, Errno::SUCCESS}; + } + } return RecvFrom(flags, message, nullptr); } @@ -114,6 +126,9 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So ASSERT(message.size() < static_cast<size_t>((std::numeric_limits<int>::max)())); std::unique_lock lock(packets_mutex); + if (closed) { + return {-1, Errno::BADF}; + } if (received_packets.empty()) { if (!blocking) { return {-1, Errno::AGAIN}; @@ -124,9 +139,12 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So // the timeout to 5s instead. An expired wait is reported like an SO_RCVTIMEO timeout. const u32 timeout = receive_timeout == 0 ? 5000 : receive_timeout; if (!packets_cv.wait_for(lock, std::chrono::milliseconds{timeout}, - [this] { return !received_packets.empty(); })) { + [this] { return closed || !received_packets.empty(); })) { return {-1, Errno::AGAIN}; } + if (closed) { + return {-1, Errno::BADF}; + } } return ReceivePacket(flags, message, addr, message.size()); @@ -217,8 +235,13 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message } Errno ProxySocket::Close() { - fd = INVALID_SOCKET; - closed = true; + { + std::lock_guard guard(packets_mutex); + fd = INVALID_SOCKET; + closed = true; + received_packets = {}; + } + packets_cv.notify_all(); return Errno::SUCCESS; } @@ -239,7 +262,10 @@ Errno ProxySocket::SetReuseAddr(bool enable) { } Errno ProxySocket::SetBroadcast(bool enable) { - broadcast = enable; + { + std::lock_guard guard(packets_mutex); + broadcast = enable; + } return SetSockOpt<u32>(fd, SO_BROADCAST, enable ? 1 : 0); } |
