Merge pull request 'service: network: Fix lobby SDK crashes and performance drops in multiplayer' (#107) from fix/mitigate-sdk-crash into main

Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/107
This commit is contained in:
Collecting
2026-01-22 05:37:18 +01:00
4 changed files with 27 additions and 50 deletions

View File

@@ -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<NodeLatestUpdate> 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<u8>(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!");

View File

@@ -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<LanStation, StationCountMax> stations;
std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
std::array<u8, NodeCountMax> node_last_states{};

View File

@@ -553,25 +553,10 @@ std::pair<s32, Errno> 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<Network::ProxySocket>(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<Network::ProxySocket>(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<Network::Socket>();
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<Network::SocketBase> 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<Network::ProxySocket>(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;
}

View File

@@ -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<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> 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 =