mirror of
https://git.eden-emu.dev/archive/citron
synced 2026-04-12 14:18:49 -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:
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
@@ -928,7 +929,13 @@ bool Socket::IsOpened() const {
|
||||
}
|
||||
|
||||
void Socket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||
LOG_WARNING(Network, "ProxyPacket received, but not in Proxy mode!");
|
||||
LOG_WARNING(Network,
|
||||
"ProxyPacket received on regular socket (not ProxySocket). "
|
||||
"This may indicate socket type mismatch. "
|
||||
"Packet from {}:{} to {}:{}, protocol={}, reliable={}",
|
||||
packet.local_endpoint.ip[0], packet.local_endpoint.portno,
|
||||
packet.remote_endpoint.ip[0], packet.remote_endpoint.portno,
|
||||
static_cast<int>(packet.protocol), packet.reliable);
|
||||
}
|
||||
|
||||
} // namespace Network
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <chrono>
|
||||
@@ -30,11 +31,15 @@ ProxySocket::~ProxySocket() {
|
||||
void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||
if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
|
||||
closed) {
|
||||
stats.packets_dropped++;
|
||||
LOG_DEBUG(Network, "Dropped packet: protocol mismatch or closed socket. Stats: sent={}, recv={}, dropped={}",
|
||||
stats.packets_sent, stats.packets_received, stats.packets_dropped);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!broadcast && packet.broadcast) {
|
||||
LOG_INFO(Network, "Received broadcast packet, but not configured for broadcast mode");
|
||||
stats.packets_dropped++;
|
||||
LOG_DEBUG(Network, "Dropped broadcast packet on non-broadcast socket");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -43,6 +48,15 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||
|
||||
std::lock_guard guard(packets_mutex);
|
||||
received_packets.push(decompressed);
|
||||
stats.packets_received++;
|
||||
stats.bytes_received += decompressed.data.size();
|
||||
|
||||
// Log statistics periodically (every 100 packets)
|
||||
if (stats.packets_received % 100 == 0) {
|
||||
LOG_DEBUG(Network, "ProxySocket stats: sent={} ({} bytes), recv={} ({} bytes), dropped={}",
|
||||
stats.packets_sent, stats.bytes_sent,
|
||||
stats.packets_received, stats.bytes_received, stats.packets_dropped);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@@ -189,10 +203,20 @@ std::pair<s32, Errno> ProxySocket::Send(std::span<const u8> message, int flags)
|
||||
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||
if (room_member->IsConnected()) {
|
||||
const size_t original_size = packet.data.size();
|
||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
||||
packet.data.size());
|
||||
room_member->SendProxyPacket(packet);
|
||||
|
||||
stats.packets_sent++;
|
||||
stats.bytes_sent += original_size;
|
||||
} else {
|
||||
LOG_WARNING(Network, "Cannot send packet: not connected to room. Total packets dropped: {}",
|
||||
++stats.packets_dropped);
|
||||
}
|
||||
} else {
|
||||
LOG_ERROR(Network, "Cannot send packet: room member unavailable");
|
||||
stats.packets_dropped++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,6 +260,14 @@ std::pair<s32, Errno> ProxySocket::SendTo(u32 flags, std::span<const u8> message
|
||||
packet.data.clear();
|
||||
std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
|
||||
|
||||
// Determine if packet should use unreliable delivery for better latency
|
||||
// Use unreliable delivery for:
|
||||
// 1. Small, frequent game data packets (< 1200 bytes for typical MTU)
|
||||
// 2. UDP protocol packets (most game traffic)
|
||||
// 3. Non-broadcast packets (broadcast should be reliable for coordination)
|
||||
const bool is_game_data = protocol == Protocol::UDP && message.size() < 1200 && !packet.broadcast;
|
||||
packet.reliable = !is_game_data;
|
||||
|
||||
SendPacket(packet);
|
||||
|
||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||
|
||||
@@ -94,6 +94,15 @@ private:
|
||||
std::mutex packets_mutex;
|
||||
|
||||
RoomNetwork& room_network;
|
||||
|
||||
// Packet statistics for monitoring
|
||||
struct PacketStatistics {
|
||||
u64 packets_sent = 0;
|
||||
u64 packets_received = 0;
|
||||
u64 packets_dropped = 0;
|
||||
u64 bytes_sent = 0;
|
||||
u64 bytes_received = 0;
|
||||
} stats;
|
||||
};
|
||||
|
||||
} // namespace Network
|
||||
|
||||
Reference in New Issue
Block a user