mirror of
https://git.citron-emu.org/citron/emulator
synced 2026-01-25 12:13:27 +00:00
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:
@@ -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!");
|
||||
|
||||
@@ -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{};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user