mirror of
https://git.citron-emu.org/citron/emulator
synced 2025-12-19 10:43:33 +00:00
Merge pull request 'feat: implement multiplayer networking improvements for reduced latency' (#27) from feature/multiplayer-networking-improvements into main
Reviewed-on: https://git.citron-emu.org/Citron/Emulator/pulls/27
This commit is contained in:
@@ -661,10 +661,12 @@ struct Values {
|
|||||||
// Network
|
// Network
|
||||||
Setting<std::string> network_interface{linkage, std::string(), "network_interface",
|
Setting<std::string> network_interface{linkage, std::string(), "network_interface",
|
||||||
Category::Network};
|
Category::Network};
|
||||||
|
Setting<std::string> lobby_api_url{linkage, "https://api.ynet-fun.xyz", "lobby_api_url",
|
||||||
|
Category::Network};
|
||||||
|
|
||||||
// WebService
|
// WebService
|
||||||
Setting<bool> enable_telemetry{linkage, false, "enable_telemetry", Category::WebService};
|
Setting<bool> enable_telemetry{linkage, false, "enable_telemetry", Category::WebService};
|
||||||
Setting<std::string> web_api_url{linkage, "http://api.ynet-fun.xyz", "web_api_url",
|
Setting<std::string> web_api_url{linkage, "https://api.ynet-fun.xyz", "web_api_url",
|
||||||
Category::WebService};
|
Category::WebService};
|
||||||
Setting<std::string> citron_username{linkage, std::string(), "citron_username",
|
Setting<std::string> citron_username{linkage, std::string(), "citron_username",
|
||||||
Category::WebService};
|
Category::WebService};
|
||||||
|
|||||||
@@ -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={}",
|
LOG_INFO(Service, "New socket fd={} domain={} type={} protocol={} proxy={}",
|
||||||
fd, domain, type, protocol, using_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) {
|
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 {
|
} 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(Translate(domain), Translate(type), Translate(protocol));
|
|
||||||
descriptor.is_connection_based = IsConnectionBased(type);
|
|
||||||
|
|
||||||
return {fd, Errno::SUCCESS};
|
return {fd, Errno::SUCCESS};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -966,13 +983,34 @@ Errno BSD::CloseImpl(s32 fd) {
|
|||||||
return Errno::BADF;
|
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) {
|
if (bsd_errno != Errno::SUCCESS) {
|
||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(Service, "Close socket fd={}", fd);
|
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();
|
file_descriptors[fd].reset();
|
||||||
return bsd_errno;
|
return bsd_errno;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,9 @@ private:
|
|||||||
std::shared_ptr<Network::SocketBase> socket;
|
std::shared_ptr<Network::SocketBase> socket;
|
||||||
s32 flags = 0;
|
s32 flags = 0;
|
||||||
bool is_connection_based = false;
|
bool is_connection_based = false;
|
||||||
|
Network::Domain domain = Network::Domain::INET;
|
||||||
|
Network::Type type = Network::Type::DGRAM;
|
||||||
|
Network::Protocol protocol = Network::Protocol::UDP;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PollWork {
|
struct PollWork {
|
||||||
@@ -209,6 +212,20 @@ private:
|
|||||||
// Callback identifier for the OnProxyPacketReceived event.
|
// Callback identifier for the OnProxyPacketReceived event.
|
||||||
Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
|
Network::RoomMember::CallbackHandle<Network::ProxyPacket> proxy_packet_received;
|
||||||
|
|
||||||
|
/// Socket pool to cache and reuse ProxySocket instances
|
||||||
|
struct SocketPoolKey {
|
||||||
|
Network::Domain domain;
|
||||||
|
Network::Type type;
|
||||||
|
Network::Protocol protocol;
|
||||||
|
|
||||||
|
bool operator<(const SocketPoolKey& other) const {
|
||||||
|
return std::tie(domain, type, protocol) <
|
||||||
|
std::tie(other.domain, other.type, other.protocol);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::map<SocketPoolKey, std::vector<std::shared_ptr<Network::SocketBase>>> socket_pool;
|
||||||
|
std::mutex socket_pool_mutex;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual std::unique_lock<std::mutex> LockService() override;
|
virtual std::unique_lock<std::mutex> LockService() override;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -928,7 +929,13 @@ bool Socket::IsOpened() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Socket::HandleProxyPacket(const ProxyPacket& packet) {
|
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
|
} // namespace Network
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -30,11 +31,15 @@ ProxySocket::~ProxySocket() {
|
|||||||
void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
||||||
if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
|
if (protocol != packet.protocol || local_endpoint.portno != packet.remote_endpoint.portno ||
|
||||||
closed) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!broadcast && packet.broadcast) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,6 +48,15 @@ void ProxySocket::HandleProxyPacket(const ProxyPacket& packet) {
|
|||||||
|
|
||||||
std::lock_guard guard(packets_mutex);
|
std::lock_guard guard(packets_mutex);
|
||||||
received_packets.push(decompressed);
|
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>
|
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) {
|
void ProxySocket::SendPacket(ProxyPacket& packet) {
|
||||||
if (auto room_member = room_network.GetRoomMember().lock()) {
|
if (auto room_member = room_network.GetRoomMember().lock()) {
|
||||||
if (room_member->IsConnected()) {
|
if (room_member->IsConnected()) {
|
||||||
|
const size_t original_size = packet.data.size();
|
||||||
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
packet.data = Common::Compression::CompressDataZSTDDefault(packet.data.data(),
|
||||||
packet.data.size());
|
packet.data.size());
|
||||||
room_member->SendProxyPacket(packet);
|
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();
|
packet.data.clear();
|
||||||
std::copy(message.begin(), message.end(), std::back_inserter(packet.data));
|
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);
|
SendPacket(packet);
|
||||||
|
|
||||||
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
return {static_cast<s32>(message.size()), Errno::SUCCESS};
|
||||||
|
|||||||
@@ -94,6 +94,15 @@ private:
|
|||||||
std::mutex packets_mutex;
|
std::mutex packets_mutex;
|
||||||
|
|
||||||
RoomNetwork& room_network;
|
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
|
} // namespace Network
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -831,10 +832,14 @@ void Room::RoomImpl::HandleProxyPacket(const ENetEvent* event) {
|
|||||||
bool broadcast;
|
bool broadcast;
|
||||||
in_packet.Read(broadcast); // Broadcast
|
in_packet.Read(broadcast); // Broadcast
|
||||||
|
|
||||||
|
bool reliable;
|
||||||
|
in_packet.Read(reliable); // Reliability flag
|
||||||
|
|
||||||
Packet out_packet;
|
Packet out_packet;
|
||||||
out_packet.Append(event->packet->data, event->packet->dataLength);
|
out_packet.Append(event->packet->data, event->packet->dataLength);
|
||||||
|
const u32 enet_flags = reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED;
|
||||||
ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
||||||
ENET_PACKET_FLAG_RELIABLE);
|
enet_flags);
|
||||||
|
|
||||||
const auto& destination_address = remote_ip;
|
const auto& destination_address = remote_ip;
|
||||||
if (broadcast) { // Send the data to everyone except the sender
|
if (broadcast) { // Send the data to everyone except the sender
|
||||||
@@ -885,10 +890,14 @@ void Room::RoomImpl::HandleLdnPacket(const ENetEvent* event) {
|
|||||||
bool broadcast;
|
bool broadcast;
|
||||||
in_packet.Read(broadcast); // Broadcast
|
in_packet.Read(broadcast); // Broadcast
|
||||||
|
|
||||||
|
bool reliable;
|
||||||
|
in_packet.Read(reliable); // Reliability flag
|
||||||
|
|
||||||
Packet out_packet;
|
Packet out_packet;
|
||||||
out_packet.Append(event->packet->data, event->packet->dataLength);
|
out_packet.Append(event->packet->data, event->packet->dataLength);
|
||||||
|
const u32 enet_flags = reliable ? ENET_PACKET_FLAG_RELIABLE : ENET_PACKET_FLAG_UNSEQUENCED;
|
||||||
ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
ENetPacket* enet_packet = enet_packet_create(out_packet.GetData(), out_packet.GetDataSize(),
|
||||||
ENET_PACKET_FLAG_RELIABLE);
|
enet_flags);
|
||||||
|
|
||||||
const auto& destination_address = remote_ip;
|
const auto& destination_address = remote_ip;
|
||||||
if (broadcast) { // Send the data to everyone except the sender
|
if (broadcast) { // Send the data to everyone except the sender
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
@@ -44,8 +45,15 @@ public:
|
|||||||
std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
|
std::mutex network_mutex; ///< Mutex that controls access to the `client` variable.
|
||||||
/// Thread that receives and dispatches network packets
|
/// Thread that receives and dispatches network packets
|
||||||
std::unique_ptr<std::thread> loop_thread;
|
std::unique_ptr<std::thread> loop_thread;
|
||||||
|
|
||||||
|
/// Structure to hold a packet and its reliability flag
|
||||||
|
struct PacketWithReliability {
|
||||||
|
Packet packet;
|
||||||
|
bool reliable;
|
||||||
|
};
|
||||||
|
|
||||||
std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
|
std::mutex send_list_mutex; ///< Mutex that controls access to the `send_list` variable.
|
||||||
std::list<Packet> send_list; ///< A list that stores all packets to send the async
|
std::list<PacketWithReliability> send_list; ///< A list that stores all packets to send the async
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using CallbackSet = std::set<CallbackHandle<T>>;
|
using CallbackSet = std::set<CallbackHandle<T>>;
|
||||||
@@ -73,10 +81,11 @@ public:
|
|||||||
void StartLoop();
|
void StartLoop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends data to the room. It will be send on channel 0 with flag RELIABLE
|
* Sends data to the room. It will be send on channel 0 with specified reliability
|
||||||
* @param packet The data to send
|
* @param packet The data to send
|
||||||
|
* @param reliable Whether to use reliable delivery (true) or unreliable/unsequenced (false)
|
||||||
*/
|
*/
|
||||||
void Send(Packet&& packet);
|
void Send(Packet&& packet, bool reliable = true);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends a request to the server, asking for permission to join a room with the specified
|
* Sends a request to the server, asking for permission to join a room with the specified
|
||||||
@@ -257,14 +266,17 @@ void RoomMember::RoomMemberImpl::MemberLoop() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::list<Packet> packets;
|
std::list<PacketWithReliability> packets;
|
||||||
{
|
{
|
||||||
std::lock_guard send_lock(send_list_mutex);
|
std::lock_guard send_lock(send_list_mutex);
|
||||||
packets.swap(send_list);
|
packets.swap(send_list);
|
||||||
}
|
}
|
||||||
for (const auto& packet : packets) {
|
for (const auto& packet_data : packets) {
|
||||||
ENetPacket* enetPacket = enet_packet_create(packet.GetData(), packet.GetDataSize(),
|
const u32 enet_flags = packet_data.reliable ? ENET_PACKET_FLAG_RELIABLE
|
||||||
ENET_PACKET_FLAG_RELIABLE);
|
: ENET_PACKET_FLAG_UNSEQUENCED;
|
||||||
|
ENetPacket* enetPacket = enet_packet_create(packet_data.packet.GetData(),
|
||||||
|
packet_data.packet.GetDataSize(),
|
||||||
|
enet_flags);
|
||||||
enet_peer_send(server, 0, enetPacket);
|
enet_peer_send(server, 0, enetPacket);
|
||||||
}
|
}
|
||||||
enet_host_flush(client);
|
enet_host_flush(client);
|
||||||
@@ -276,9 +288,9 @@ void RoomMember::RoomMemberImpl::StartLoop() {
|
|||||||
loop_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::MemberLoop, this);
|
loop_thread = std::make_unique<std::thread>(&RoomMember::RoomMemberImpl::MemberLoop, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::Send(Packet&& packet) {
|
void RoomMember::RoomMemberImpl::Send(Packet&& packet, bool reliable) {
|
||||||
std::lock_guard lock(send_list_mutex);
|
std::lock_guard lock(send_list_mutex);
|
||||||
send_list.push_back(std::move(packet));
|
send_list.push_back({std::move(packet), reliable});
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_,
|
void RoomMember::RoomMemberImpl::SendJoinRequest(const std::string& nickname_,
|
||||||
@@ -377,6 +389,7 @@ void RoomMember::RoomMemberImpl::HandleProxyPackets(const ENetEvent* event) {
|
|||||||
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
|
proxy_packet.protocol = static_cast<Protocol>(protocol_type);
|
||||||
|
|
||||||
packet.Read(proxy_packet.broadcast);
|
packet.Read(proxy_packet.broadcast);
|
||||||
|
packet.Read(proxy_packet.reliable);
|
||||||
packet.Read(proxy_packet.data);
|
packet.Read(proxy_packet.data);
|
||||||
|
|
||||||
Invoke<ProxyPacket>(proxy_packet);
|
Invoke<ProxyPacket>(proxy_packet);
|
||||||
@@ -397,6 +410,7 @@ void RoomMember::RoomMemberImpl::HandleLdnPackets(const ENetEvent* event) {
|
|||||||
packet.Read(ldn_packet.local_ip);
|
packet.Read(ldn_packet.local_ip);
|
||||||
packet.Read(ldn_packet.remote_ip);
|
packet.Read(ldn_packet.remote_ip);
|
||||||
packet.Read(ldn_packet.broadcast);
|
packet.Read(ldn_packet.broadcast);
|
||||||
|
packet.Read(ldn_packet.reliable);
|
||||||
|
|
||||||
packet.Read(ldn_packet.data);
|
packet.Read(ldn_packet.data);
|
||||||
|
|
||||||
@@ -638,9 +652,10 @@ void RoomMember::SendProxyPacket(const ProxyPacket& proxy_packet) {
|
|||||||
|
|
||||||
packet.Write(static_cast<u8>(proxy_packet.protocol));
|
packet.Write(static_cast<u8>(proxy_packet.protocol));
|
||||||
packet.Write(proxy_packet.broadcast);
|
packet.Write(proxy_packet.broadcast);
|
||||||
|
packet.Write(proxy_packet.reliable);
|
||||||
packet.Write(proxy_packet.data);
|
packet.Write(proxy_packet.data);
|
||||||
|
|
||||||
room_member_impl->Send(std::move(packet));
|
room_member_impl->Send(std::move(packet), proxy_packet.reliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) {
|
void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) {
|
||||||
@@ -652,10 +667,11 @@ void RoomMember::SendLdnPacket(const LDNPacket& ldn_packet) {
|
|||||||
packet.Write(ldn_packet.local_ip);
|
packet.Write(ldn_packet.local_ip);
|
||||||
packet.Write(ldn_packet.remote_ip);
|
packet.Write(ldn_packet.remote_ip);
|
||||||
packet.Write(ldn_packet.broadcast);
|
packet.Write(ldn_packet.broadcast);
|
||||||
|
packet.Write(ldn_packet.reliable);
|
||||||
|
|
||||||
packet.Write(ldn_packet.data);
|
packet.Write(ldn_packet.data);
|
||||||
|
|
||||||
room_member_impl->Send(std::move(packet));
|
room_member_impl->Send(std::move(packet), ldn_packet.reliable);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RoomMember::SendChatMessage(const std::string& message) {
|
void RoomMember::SendChatMessage(const std::string& message) {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
// SPDX-FileCopyrightText: Copyright 2017 Citra Emulator Project
|
||||||
|
// SPDX-FileCopyrightText: Copyright 2025 citron Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
@@ -31,6 +32,7 @@ struct LDNPacket {
|
|||||||
IPv4Address local_ip;
|
IPv4Address local_ip;
|
||||||
IPv4Address remote_ip;
|
IPv4Address remote_ip;
|
||||||
bool broadcast;
|
bool broadcast;
|
||||||
|
bool reliable = true; // Control packets use reliable delivery by default
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,6 +42,7 @@ struct ProxyPacket {
|
|||||||
SockAddrIn remote_endpoint;
|
SockAddrIn remote_endpoint;
|
||||||
Protocol protocol;
|
Protocol protocol;
|
||||||
bool broadcast;
|
bool broadcast;
|
||||||
|
bool reliable = true; // Use reliable delivery by default for compatibility
|
||||||
std::vector<u8> data;
|
std::vector<u8> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user