mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-14 16:50:46 -04: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 {
|
Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network) const {
|
||||||
|
std::scoped_lock lock{packet_mutex};
|
||||||
if (state == State::AccessPointCreated || state == State::StationConnected) {
|
if (state == State::AccessPointCreated || state == State::StationConnected) {
|
||||||
std::memcpy(&out_network, &network_info, sizeof(network_info));
|
std::memcpy(&out_network, &network_info, sizeof(network_info));
|
||||||
return ResultSuccess;
|
return ResultSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResultBadState;
|
return ResultBadState;
|
||||||
}
|
}
|
||||||
|
|
||||||
Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
|
Result LANDiscovery::GetNetworkInfo(NetworkInfo& out_network,
|
||||||
std::span<NodeLatestUpdate> out_updates) {
|
std::span<NodeLatestUpdate> out_updates) {
|
||||||
|
std::scoped_lock lock{packet_mutex};
|
||||||
if (out_updates.size() > NodeCountMax) {
|
if (out_updates.size() > NodeCountMax) {
|
||||||
return ResultInvalidBufferCount;
|
return ResultInvalidBufferCount;
|
||||||
}
|
}
|
||||||
@@ -376,7 +377,9 @@ void LANDiscovery::UpdateNodes() {
|
|||||||
}
|
}
|
||||||
station.OverrideInfo();
|
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) {
|
for (auto local_ip : connected_clients) {
|
||||||
SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip);
|
SendPacket(Network::LDNPacketType::SyncNetwork, network_info, local_ip);
|
||||||
@@ -387,6 +390,7 @@ void LANDiscovery::UpdateNodes() {
|
|||||||
|
|
||||||
void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) {
|
void LANDiscovery::OnSyncNetwork(const NetworkInfo& info) {
|
||||||
network_info = info;
|
network_info = info;
|
||||||
|
|
||||||
if (state == State::StationOpened) {
|
if (state == State::StationOpened) {
|
||||||
SetState(State::StationConnected);
|
SetState(State::StationConnected);
|
||||||
}
|
}
|
||||||
@@ -567,10 +571,20 @@ void LANDiscovery::ReceivePacket(const Network::LDNPacket& packet) {
|
|||||||
}
|
}
|
||||||
case Network::LDNPacketType::SyncNetwork: {
|
case Network::LDNPacketType::SyncNetwork: {
|
||||||
if (state == State::StationOpened || state == State::StationConnected) {
|
if (state == State::StationOpened || state == State::StationConnected) {
|
||||||
LOG_INFO(Frontend, "SyncNetwork packet received!");
|
if (packet.data.size() < sizeof(NetworkInfo)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
NetworkInfo info{};
|
NetworkInfo info{};
|
||||||
std::memcpy(&info, packet.data.data(), sizeof(NetworkInfo));
|
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);
|
OnSyncNetwork(info);
|
||||||
} else {
|
} else {
|
||||||
LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");
|
LOG_INFO(Frontend, "SyncNetwork packet received but in wrong State!");
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2026 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -112,7 +113,7 @@ protected:
|
|||||||
static constexpr Ssid fake_ssid{"CitronFakeSsidForLdn"};
|
static constexpr Ssid fake_ssid{"CitronFakeSsidForLdn"};
|
||||||
|
|
||||||
bool inited{};
|
bool inited{};
|
||||||
std::mutex packet_mutex;
|
mutable std::mutex packet_mutex;
|
||||||
std::array<LanStation, StationCountMax> stations;
|
std::array<LanStation, StationCountMax> stations;
|
||||||
std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
|
std::array<NodeLatestUpdate, NodeCountMax> node_changes{};
|
||||||
std::array<u8, NodeCountMax> node_last_states{};
|
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.protocol = Translate(protocol);
|
||||||
descriptor.is_connection_based = IsConnectionBased(type);
|
descriptor.is_connection_based = IsConnectionBased(type);
|
||||||
|
|
||||||
// Try to reuse a socket from the pool if using proxy
|
|
||||||
if (using_proxy) {
|
if (using_proxy) {
|
||||||
SocketPoolKey key{descriptor.domain, descriptor.type, descriptor.protocol};
|
descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
|
||||||
std::lock_guard lock(socket_pool_mutex);
|
descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol);
|
||||||
|
LOG_DEBUG(Service, "Created new ProxySocket for fd={}", fd);
|
||||||
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);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
descriptor.socket = std::make_shared<Network::Socket>();
|
descriptor.socket = std::make_shared<Network::Socket>();
|
||||||
descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol);
|
descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol);
|
||||||
@@ -1008,39 +993,17 @@ Errno BSD::CloseImpl(s32 fd) {
|
|||||||
return Errno::BADF;
|
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;
|
std::shared_ptr<Network::SocketBase> socket_to_close;
|
||||||
SocketPoolKey key;
|
|
||||||
bool is_proxy = false;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
std::lock_guard lock(fd_table_mutex);
|
std::lock_guard lock(fd_table_mutex);
|
||||||
auto& descriptor = file_descriptors[fd];
|
socket_to_close = file_descriptors[fd]->socket;
|
||||||
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
|
|
||||||
file_descriptors[fd].reset();
|
file_descriptors[fd].reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Errno bsd_errno = Translate(socket_to_close->Close());
|
const Errno bsd_errno = Translate(socket_to_close->Close());
|
||||||
if (bsd_errno != Errno::SUCCESS) {
|
|
||||||
return bsd_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG_INFO(Service, "Close socket fd={}", fd);
|
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;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,14 +34,13 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|||||||
|
|
||||||
const auto my_ip = room_member->GetFakeIpAddress();
|
const auto my_ip = room_member->GetFakeIpAddress();
|
||||||
|
|
||||||
// If the sender (local_endpoint) is OUR IP, ignore it.
|
// 1. Ignore our own echo
|
||||||
// We don't want to process our own sent packets.
|
|
||||||
if (packet.local_endpoint.ip == my_ip) {
|
if (packet.local_endpoint.ip == my_ip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only accept packets meant for us or actual broadcasts.
|
// 2. Ignore packets meant for other people
|
||||||
if (packet.remote_endpoint.ip != my_ip && !packet.broadcast) {
|
if (!packet.broadcast && packet.remote_endpoint.ip != my_ip) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +172,7 @@ std::pair<s32, Errno> ProxySocket::RecvFrom(int flags, std::span<u8> message, So
|
|||||||
return {-1, Errno::AGAIN};
|
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 = std::chrono::steady_clock::now() - timestamp;
|
||||||
const auto time_diff_ms =
|
const auto time_diff_ms =
|
||||||
|
|||||||
Reference in New Issue
Block a user