mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-09 04:38:50 -04:00
feat: implement multiplayer networking improvements for reduced latency
Major networking enhancements to improve multiplayer performance and reduce desync issues in games like Mario Kart 8 Deluxe: Network Performance: - Add socket connection pooling in BSD service to reduce overhead - Implement unreliable packet delivery for latency-sensitive game data - Add packet reliability control for both ProxyPacket and LDNPacket - Use ENET_PACKET_FLAG_UNSEQUENCED for small UDP packets (<1200 bytes) Monitoring & Debugging: - Add PacketStatistics struct to track sent/received/dropped packets - Enhanced logging for proxy packet handling and socket lifecycle - Periodic stats logging every 100 packets for diagnostics Configuration: - Update lobby_api_url and web_api_url to https://api.ynet-fun.xyz - Add lobby API URL configuration support Socket Management: - Implement SocketPoolKey for efficient socket reuse - Store domain/type/protocol info in FileDescriptor - Max pool size limit (8 sockets per type) to prevent memory bloat - Return closed sockets to pool when room is still connected Protocol Updates: - Add 'reliable' field to ProxyPacket and LDNPacket structures - Update room.cpp packet handlers to respect reliability flags - Maintain backward compatibility with default reliable=true These changes significantly reduce packet latency for real-time game traffic while maintaining reliability for control packets. Signed-off-by: Zephyron <zephyron@citron-emu.org>
This commit is contained in:
@@ -547,15 +547,32 @@ std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protoco
|
||||
LOG_INFO(Service, "New socket fd={} domain={} type={} protocol={} proxy={}",
|
||||
fd, domain, type, protocol, using_proxy);
|
||||
|
||||
// Store socket type information for pooling
|
||||
descriptor.domain = Translate(domain);
|
||||
descriptor.type = Translate(type);
|
||||
descriptor.protocol = Translate(protocol);
|
||||
descriptor.is_connection_based = IsConnectionBased(type);
|
||||
|
||||
// Try to reuse a socket from the pool if using proxy
|
||||
if (using_proxy) {
|
||||
descriptor.socket = std::make_shared<Network::ProxySocket>(room_network);
|
||||
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();
|
||||
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 {
|
||||
descriptor.socket = std::make_shared<Network::Socket>();
|
||||
descriptor.socket->Initialize(descriptor.domain, descriptor.type, descriptor.protocol);
|
||||
}
|
||||
|
||||
descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(protocol));
|
||||
descriptor.is_connection_based = IsConnectionBased(type);
|
||||
|
||||
return {fd, Errno::SUCCESS};
|
||||
}
|
||||
|
||||
@@ -966,13 +983,34 @@ Errno BSD::CloseImpl(s32 fd) {
|
||||
return Errno::BADF;
|
||||
}
|
||||
|
||||
const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
|
||||
auto& descriptor = file_descriptors[fd];
|
||||
const Errno bsd_errno = Translate(descriptor->socket->Close());
|
||||
if (bsd_errno != Errno::SUCCESS) {
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
LOG_INFO(Service, "Close socket fd={}", fd);
|
||||
|
||||
// Try to return ProxySocket to the pool for reuse
|
||||
auto proxy_socket = std::dynamic_pointer_cast<Network::ProxySocket>(descriptor->socket);
|
||||
auto room_member = room_network.GetRoomMember().lock();
|
||||
|
||||
if (proxy_socket && room_member && room_member->IsConnected()) {
|
||||
// Socket is still valid, add to pool
|
||||
std::lock_guard lock(socket_pool_mutex);
|
||||
|
||||
SocketPoolKey key{descriptor->domain, descriptor->type, descriptor->protocol};
|
||||
|
||||
// Limit pool size to avoid memory bloat (max 8 sockets per type)
|
||||
constexpr size_t MAX_POOL_SIZE = 8;
|
||||
if (socket_pool[key].size() < MAX_POOL_SIZE) {
|
||||
socket_pool[key].push_back(descriptor->socket);
|
||||
LOG_DEBUG(Service, "Returned socket fd={} to pool", fd);
|
||||
} else {
|
||||
LOG_DEBUG(Service, "Socket pool full, destroying socket fd={}", fd);
|
||||
}
|
||||
}
|
||||
|
||||
file_descriptors[fd].reset();
|
||||
return bsd_errno;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user