Skip to content

Commit 984d938

Browse files
committed
Refactored the way the ping (rtt) is taken to read it from ENet.
1 parent 3c7f5a0 commit 984d938

File tree

9 files changed

+105
-63
lines changed

9 files changed

+105
-63
lines changed

core/net_utilities.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
#include <limits>
99
#include <memory>
1010

11-
void NS::PeerData::set_latency(int p_latency) {
12-
compressed_latency = std::round(float(std::clamp(p_latency, 0, 1000)) / 4.0);
11+
void NS::PeerData::set_latency(float p_latency) {
12+
compressed_latency = std::round(std::clamp(p_latency, 0.f, 1000.0f) / 4.0);
1313
}
1414

15-
int NS::PeerData::get_latency() const {
16-
return int(compressed_latency) * 4;
15+
float NS::PeerData::get_latency() const {
16+
return compressed_latency * 4.0;
1717
}
1818

1919
void NS::PeerData::make_controller() {

core/net_utilities.h

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -438,18 +438,37 @@ struct PeerData {
438438
PeerAuthorityData authority_data;
439439

440440
private:
441+
/// Get latency (ping): The round trip time a packet takes to go and return back.
441442
std::uint8_t compressed_latency = 0;
442443

444+
/// Get OUT packetloss in %
445+
float out_packet_loss_percentage = 0.0;
446+
447+
/// Current jitter for this connection in milliseconds.
448+
/// Jitter represents the average time divergence of all sent packets.
449+
/// Ex:
450+
/// - If the time between the sending and the reception of packets is always
451+
/// 100ms; the jitter will be 0.
452+
/// - If the time difference is either 150ms or 100ms, the jitter will tend
453+
/// towards 50ms.
454+
float average_jitter_in_ms = 0.0;
455+
443456
public:
444457
// In ms
445-
void set_latency(int p_ping);
458+
void set_latency(float p_ping);
446459

447460
// In ms
448-
int get_latency() const;
461+
float get_latency() const;
449462

450463
void set_compressed_latency(std::uint8_t p_compressed_latency) { compressed_latency = p_compressed_latency; }
451464
std::uint8_t get_compressed_latency() const { return compressed_latency; }
452465

466+
void set_out_packet_loss_percentage(float p_packet_loss) { out_packet_loss_percentage = p_packet_loss; }
467+
float get_out_packet_loss_percentage() const { return out_packet_loss_percentage; }
468+
469+
void set_average_jitter_in_ms(float p_jitter_ms) { average_jitter_in_ms = p_jitter_ms; }
470+
float get_average_jitter_in_ms() const { return average_jitter_in_ms; }
471+
453472
void make_controller();
454473
PeerNetworkedController *get_controller() {
455474
return controller.get();
@@ -466,9 +485,8 @@ struct PeerServerData {
466485
// For new peers a full snapshot is needed.
467486
bool need_full_snapshot = true;
468487

469-
// The latency, in ms, between this peer and the server.
470-
std::chrono::high_resolution_clock::time_point latency_ping_timestamp;
471-
bool latency_calculation_in_progress = false;
488+
// How much time (seconds) from the latest net_stats update.
489+
float netstats_update_sec = 0.0;
472490
};
473491

474492
struct SyncGroup {

core/network_interface.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "core.h"
44
#include "ensure.h"
5+
#include "net_utilities.h"
56
#include "network_codec.h"
67
#include <functional>
78
#include <string>
@@ -46,7 +47,7 @@ class NetworkInterface {
4647
public:
4748
virtual ~NetworkInterface() = default;
4849

49-
public: // ---------------------------------------------------------------- APIs
50+
public: // ----------------------------------------------------------- Interface
5051
virtual void clear() {
5152
rpcs_info.clear();
5253
rpc_last_sender = 0;
@@ -78,6 +79,12 @@ class NetworkInterface {
7879
/// Can be used to verify if the local peer is the server.
7980
virtual bool is_local_peer_server() const = 0;
8081

82+
/// This function is called by the `SceneSynchronizer` to update
83+
/// the network `stats` for the given peer.
84+
/// NOTE: This function is called only on the server.
85+
virtual void server_update_net_stats(int p_peer, PeerData &r_peer_data) const = 0;
86+
87+
public: // ---------------------------------------------------------------- APIs
8188
/// Can be used to verify if the local peer is the authority of this unit.
8289
virtual bool is_local_peer_authority_of_this_unit() const {
8390
return get_unit_authority() == fetch_local_peer_id();

godot4/gd_network_interface.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,42 @@ void GdNetworkInterface::gd_rpc_receive(const Vector<uint8_t> &p_buffer) {
297297
owner->get_multiplayer()->get_remote_sender_id(),
298298
db);
299299
}
300+
301+
// Copied from `enet_packet_peer.h` to avoid including it, which is complex due to its dependency with enet.
302+
enum PeerStatistic {
303+
PEER_PACKET_LOSS,
304+
PEER_PACKET_LOSS_VARIANCE,
305+
PEER_PACKET_LOSS_EPOCH,
306+
PEER_ROUND_TRIP_TIME,
307+
PEER_ROUND_TRIP_TIME_VARIANCE,
308+
PEER_LAST_ROUND_TRIP_TIME,
309+
PEER_LAST_ROUND_TRIP_TIME_VARIANCE,
310+
PEER_PACKET_THROTTLE,
311+
PEER_PACKET_THROTTLE_LIMIT,
312+
PEER_PACKET_THROTTLE_COUNTER,
313+
PEER_PACKET_THROTTLE_EPOCH,
314+
PEER_PACKET_THROTTLE_ACCELERATION,
315+
PEER_PACKET_THROTTLE_DECELERATION,
316+
PEER_PACKET_THROTTLE_INTERVAL,
317+
};
318+
319+
// Copied from enet.h to avoid including it.
320+
uint64_t ENET_PEER_PACKET_LOSS_SCALE = (1 << 16);
321+
322+
void GdNetworkInterface::server_update_net_stats(int p_peer, NS::PeerData &r_peer_data) const {
323+
// This function is always called on the server.
324+
ASSERT_COND(is_local_peer_server());
325+
326+
ERR_FAIL_COND(!owner);
327+
ERR_FAIL_COND(!owner->get_multiplayer().is_valid());
328+
ERR_FAIL_COND(!owner->get_multiplayer()->get_multiplayer_peer().is_valid());
329+
330+
Ref<MultiplayerPeer> packet_peer = owner->get_multiplayer()->get_multiplayer_peer();
331+
Ref<PacketPeer> enet_peer = packet_peer->call("get_peer", p_peer);
332+
ERR_FAIL_COND(!enet_peer.is_valid());
333+
334+
// Using the GDScript bindings to read these so to avoid including `enet_packet_peer.h`, which is complex due to its dependency with enet.
335+
r_peer_data.set_latency(enet_peer->call("get_statistic", PEER_ROUND_TRIP_TIME));
336+
r_peer_data.set_out_packet_loss_percentage(double(enet_peer->call("get_statistic", PEER_PACKET_LOSS)) / double(ENET_PEER_PACKET_LOSS_SCALE));
337+
r_peer_data.set_average_jitter_in_ms(enet_peer->call("get_statistic", PEER_ROUND_TRIP_TIME_VARIANCE));
338+
}

godot4/gd_network_interface.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class GdNetworkInterface final : public NS::NetworkInterface,
5656

5757
virtual void rpc_send(int p_peer_recipient, bool p_reliable, DataBuffer &&p_buffer) override;
5858
void gd_rpc_receive(const Vector<uint8_t> &p_args);
59+
60+
virtual void server_update_net_stats(int p_peer, NS::PeerData &r_peer_data) const override;
5961
};
6062

6163
namespace NS_GD_Test {

scene_synchronizer.cpp

Lines changed: 13 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
#include "core/snapshot.h"
1313
#include "core/var_data.h"
1414
#include "data_buffer.h"
15-
#include <chrono>
1615
#include <limits>
1716
#include <string>
1817
#include <vector>
@@ -67,12 +66,6 @@ void SceneSynchronizerBase::setup(SynchronizerManager &p_synchronizer_interface)
6766
[this](int p_peer) { on_peer_connected(p_peer); },
6867
[this](int p_peer) { on_peer_disconnected(p_peer); });
6968

70-
rpc_handler_latency =
71-
network_interface->rpc_config(
72-
std::function<void()>(std::bind(&SceneSynchronizerBase::rpc_latency, this)),
73-
true,
74-
false);
75-
7669
rpc_handler_state =
7770
network_interface->rpc_config(
7871
std::function<void(DataBuffer &)>(std::bind(&SceneSynchronizerBase::rpc_receive_state, this, std::placeholders::_1)),
@@ -1092,18 +1085,6 @@ void SceneSynchronizerBase::notify_controller_control_mode_changed(PeerNetworked
10921085
}
10931086
}
10941087

1095-
void SceneSynchronizerBase::rpc_latency() {
1096-
if (is_client()) {
1097-
// This is a client, latency the server back.
1098-
rpc_handler_latency.rpc(get_network_interface(), get_network_interface().get_server_peer());
1099-
} else if (is_server()) {
1100-
const int sender_peer = get_network_interface().rpc_get_sender();
1101-
static_cast<ServerSynchronizer *>(synchronizer)->notify_latency_received(sender_peer);
1102-
} else {
1103-
ENSURE_MSG(false, "[FATAL] The rpc latency function was executed on a peer that is not a client nor a server. This is a bug.");
1104-
}
1105-
}
1106-
11071088
void SceneSynchronizerBase::rpc_receive_state(DataBuffer &p_snapshot) {
11081089
ENSURE_MSG(is_client(), "Only clients are suposed to receive the server snapshot.");
11091090
static_cast<ClientSynchronizer *>(synchronizer)->receive_snapshot(p_snapshot);
@@ -1732,7 +1713,7 @@ void ServerSynchronizer::process(double p_delta) {
17321713
}
17331714

17341715
process_trickled_sync(p_delta);
1735-
process_latency_update();
1716+
update_peers_net_statistics(p_delta);
17361717
process_adjust_clients_controller_tick_rate(p_delta);
17371718

17381719
SceneSynchronizerDebugger::singleton()->scene_sync_process_end(scene_synchronizer);
@@ -2368,46 +2349,29 @@ void ServerSynchronizer::process_trickled_sync(double p_delta) {
23682349
memdelete(tmp_buffer);
23692350
}
23702351

2371-
void ServerSynchronizer::process_latency_update() {
2372-
const std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
2373-
2352+
void ServerSynchronizer::update_peers_net_statistics(double p_delta) {
23742353
for (auto &[peer, peer_data] : scene_synchronizer->get_peers()) {
23752354
if (peer == scene_synchronizer->get_network_interface().fetch_local_peer_id()) {
23762355
// No need to update the ping for `self` (the server).
23772356
continue;
23782357
}
23792358

23802359
std::map<int, PeerServerData>::iterator peer_server_data_it = NS::MapFunc::insert_if_new(peers_data, peer, PeerServerData());
2381-
if (peer_server_data_it->second.latency_calculation_in_progress) {
2360+
peer_server_data_it->second.netstats_update_sec += p_delta;
2361+
2362+
if make_likely (peer_server_data_it->second.netstats_update_sec < scene_synchronizer->latency_update_rate) {
23822363
continue;
23832364
}
2384-
const auto interval = std::chrono::duration_cast<std::chrono::milliseconds>(now - peer_server_data_it->second.latency_ping_timestamp);
2385-
if (interval.count() >= (scene_synchronizer->latency_update_rate * 1000.0)) {
2386-
scene_synchronizer->rpc_handler_latency.rpc(
2387-
scene_synchronizer->get_network_interface(),
2388-
peer);
2389-
peer_server_data_it->second.latency_ping_timestamp = now;
2390-
peer_server_data_it->second.latency_calculation_in_progress = true;
2391-
}
2392-
}
2393-
}
23942365

2395-
void ServerSynchronizer::notify_latency_received(int p_peer) {
2396-
const std::chrono::high_resolution_clock::time_point now = std::chrono::high_resolution_clock::now();
2397-
2398-
std::map<int, PeerData>::iterator pd_it = NS::MapFunc::insert_if_new(scene_synchronizer->peer_data, p_peer, PeerData());
2399-
std::map<int, PeerServerData>::iterator psd_it = NS::MapFunc::insert_if_new(peers_data, p_peer, PeerServerData());
2400-
2401-
const auto rtt_interval = std::chrono::duration_cast<std::chrono::milliseconds>(now - psd_it->second.latency_ping_timestamp);
2402-
// Clamlatency to 1k as 1k ms latency is way too high to matter anyway.
2403-
const std::uint64_t rtt = rtt_interval.count();
2404-
pd_it->second.set_latency(rtt);
2405-
psd_it->second.latency_calculation_in_progress = false;
2406-
psd_it->second.latency_ping_timestamp = now;
2366+
// Time to update the network stats for this peer.
2367+
scene_synchronizer->get_network_interface().server_update_net_stats(peer, peer_data);
2368+
// Notify all sync groups about this peer having newly calculated latency.
2369+
for (auto &group : sync_groups) {
2370+
group.notify_peer_has_newly_calculated_latency(peer);
2371+
}
24072372

2408-
// Notify all sync groups about this peer having newly calculated latency.
2409-
for (auto &group : sync_groups) {
2410-
group.notify_peer_has_newly_calculated_latency(p_peer);
2373+
// Reset the timer.
2374+
peer_server_data_it->second.netstats_update_sec = 0.0;
24112375
}
24122376
}
24132377

scene_synchronizer.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,6 @@ class SceneSynchronizerBase {
162162
class NetworkInterface *network_interface = nullptr;
163163
SynchronizerManager *synchronizer_manager = nullptr;
164164

165-
RpcHandle<> rpc_handler_latency;
166165
RpcHandle<DataBuffer &> rpc_handler_state;
167166
RpcHandle<> rpc_handler_notify_need_full_snapshot;
168167
RpcHandle<bool> rpc_handler_set_network_enabled;
@@ -348,7 +347,6 @@ class SceneSynchronizerBase {
348347
bool is_variable_registered(ObjectLocalId p_id, const std::string &p_variable) const;
349348

350349
public: // ---------------------------------------------------------------- RPCs
351-
void rpc_latency();
352350
void rpc_receive_state(DataBuffer &p_snapshot);
353351
void rpc__notify_need_full_snapshot();
354352
void rpc_set_network_enabled(bool p_enabled);
@@ -672,8 +670,7 @@ class ServerSynchronizer final : public Synchronizer {
672670
DataBuffer &r_snapshot_db) const;
673671

674672
void process_trickled_sync(double p_delta);
675-
void process_latency_update();
676-
void notify_latency_received(int p_peer);
673+
void update_peers_net_statistics(double p_delta);
677674

678675
/// This function updates the `tick_additional_fps` so that the `frames_inputs`
679676
/// size is enough to reduce the missing packets to 0.

tests/local_network.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@ void LocalNetworkInterface::rpc_send(int p_peer_recipient, bool p_reliable, Data
195195
network->rpc_send(get_owner_name(), p_peer_recipient, p_reliable, std::move(p_data_buffer));
196196
}
197197

198+
void LocalNetworkInterface::server_update_net_stats(int p_peer, PeerData &r_peer_data) const {
199+
if (!network || !network->network_properties) {
200+
r_peer_data.set_latency(0);
201+
r_peer_data.set_out_packet_loss_percentage(0);
202+
r_peer_data.set_average_jitter_in_ms(0);
203+
} else {
204+
r_peer_data.set_latency(network->network_properties->rtt_seconds * 1000.0);
205+
r_peer_data.set_out_packet_loss_percentage(network->network_properties->packet_loss);
206+
// There are no statistic about this, assuming 10% of rtt.
207+
r_peer_data.set_average_jitter_in_ms(r_peer_data.get_latency() * 0.1);
208+
}
209+
}
210+
198211
NS_NAMESPACE_END
199212

200213
/// Test that the LocalNetwork is able to sync stuff.

tests/local_network.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class LocalNetworkInterface final : public NS::NetworkInterface {
109109
virtual bool is_local_peer_server() const override;
110110

111111
virtual void rpc_send(int p_peer_recipient, bool p_reliable, DataBuffer &&p_data_buffer) override;
112+
113+
virtual void server_update_net_stats(int p_peer, PeerData &r_peer_data) const override;
112114
};
113115

114116
NS_NAMESPACE_END

0 commit comments

Comments
 (0)