From 7564bd0fcba1420e72fc5626a14c1a0c9b15c7ca Mon Sep 17 00:00:00 2001 From: staphen Date: Mon, 30 Jun 2025 18:00:36 -0400 Subject: [PATCH] Introduce DvlNet function to read latency data from providers --- Source/dvlnet/abstract_net.h | 5 +++ Source/dvlnet/base.cpp | 8 +++++ Source/dvlnet/base.h | 2 ++ Source/dvlnet/base_protocol.h | 11 +++++++ Source/dvlnet/cdwrap.cpp | 5 +++ Source/dvlnet/cdwrap.h | 1 + Source/dvlnet/protocol_zt.cpp | 55 +++++++++++++++++++++---------- Source/dvlnet/protocol_zt.h | 4 ++- Source/dvlnet/zerotier_native.cpp | 12 +++++++ Source/dvlnet/zerotier_native.h | 1 + Source/storm/storm_net.cpp | 6 ++++ Source/storm/storm_net.hpp | 8 +++++ 12 files changed, 100 insertions(+), 18 deletions(-) diff --git a/Source/dvlnet/abstract_net.h b/Source/dvlnet/abstract_net.h index e6deeaac4..e3d0aad4d 100644 --- a/Source/dvlnet/abstract_net.h +++ b/Source/dvlnet/abstract_net.h @@ -56,6 +56,11 @@ public: return std::vector(); } + virtual DvlNetLatencies get_latencies(uint8_t playerid) + { + return {}; + } + static std::unique_ptr MakeNet(provider_t provider); }; diff --git a/Source/dvlnet/base.cpp b/Source/dvlnet/base.cpp index 90e37b738..9ecadbce9 100644 --- a/Source/dvlnet/base.cpp +++ b/Source/dvlnet/base.cpp @@ -33,6 +33,14 @@ void base::clear_password() pktfty = std::make_unique(); } +DvlNetLatencies base::get_latencies(uint8_t playerid) +{ + DvlNetLatencies latencies = abstract_net::get_latencies(playerid); + const PlayerState &playerState = playerStateTable_[playerid]; + latencies.echoLatency = playerState.roundTripLatency; + return latencies; +} + void base::RunEventHandler(_SNETEVENT &ev) { auto f = registered_handlers[static_cast(ev.eventid)]; diff --git a/Source/dvlnet/base.h b/Source/dvlnet/base.h index afd4b371d..4144f3440 100644 --- a/Source/dvlnet/base.h +++ b/Source/dvlnet/base.h @@ -39,6 +39,8 @@ public: void setup_password(std::string pw) override; void clear_password() override; + DvlNetLatencies get_latencies(uint8_t playerid) override; + ~base() override = default; protected: diff --git a/Source/dvlnet/base_protocol.h b/Source/dvlnet/base_protocol.h index d5b6da7c5..f4365683f 100644 --- a/Source/dvlnet/base_protocol.h +++ b/Source/dvlnet/base_protocol.h @@ -37,6 +37,7 @@ public: bool send_info_request() override; void clear_gamelist() override; std::vector get_gamelist() override; + DvlNetLatencies get_latencies(uint8_t playerid) override; ~base_protocol() override = default; @@ -557,6 +558,16 @@ std::vector base_protocol

::get_gamelist() return ret; } +template +DvlNetLatencies base_protocol

::get_latencies(uint8_t playerid) +{ + DvlNetLatencies latencies = base::get_latencies(playerid); + Peer &srcPeer = peers[playerid]; + latencies.providerLatency = proto.get_latency_to(srcPeer.endpoint); + latencies.isRelayed = proto.is_peer_relayed(srcPeer.endpoint); + return latencies; +} + template bool base_protocol

::SNetLeaveGame(int type) { diff --git a/Source/dvlnet/cdwrap.cpp b/Source/dvlnet/cdwrap.cpp index 3d6864d05..00f048b50 100644 --- a/Source/dvlnet/cdwrap.cpp +++ b/Source/dvlnet/cdwrap.cpp @@ -130,4 +130,9 @@ void cdwrap::clear_password() return dvlnet_wrap->clear_password(); } +DvlNetLatencies cdwrap::get_latencies(uint8_t playerid) +{ + return dvlnet_wrap->get_latencies(playerid); +} + } // namespace devilution::net diff --git a/Source/dvlnet/cdwrap.h b/Source/dvlnet/cdwrap.h index 1c67e35b8..15f65fc82 100644 --- a/Source/dvlnet/cdwrap.h +++ b/Source/dvlnet/cdwrap.h @@ -52,6 +52,7 @@ public: std::vector get_gamelist() override; void setup_password(std::string pw) override; void clear_password() override; + DvlNetLatencies get_latencies(uint8_t playerid) override; virtual ~cdwrap() = default; }; diff --git a/Source/dvlnet/protocol_zt.cpp b/Source/dvlnet/protocol_zt.cpp index 54b9f6839..b6378e942 100644 --- a/Source/dvlnet/protocol_zt.cpp +++ b/Source/dvlnet/protocol_zt.cpp @@ -1,5 +1,6 @@ #include "dvlnet/protocol_zt.h" +#include #include #ifdef USE_SDL3 @@ -26,6 +27,31 @@ namespace devilution { namespace net { +namespace { + +bool GetMAC(const protocol_zt::endpoint &peer, uint64_t &mac) +{ + ip6_addr_t address = {}; + IP6_ADDR_PART(&address, 0, peer.addr[0], peer.addr[1], peer.addr[2], peer.addr[3]); + IP6_ADDR_PART(&address, 1, peer.addr[4], peer.addr[5], peer.addr[6], peer.addr[7]); + IP6_ADDR_PART(&address, 2, peer.addr[8], peer.addr[9], peer.addr[10], peer.addr[11]); + IP6_ADDR_PART(&address, 3, peer.addr[12], peer.addr[13], peer.addr[14], peer.addr[15]); + + const u8_t *hwaddr; + if (nd6_get_next_hop_addr_or_queue(netif_default, nullptr, &address, &hwaddr) != ERR_OK) + return false; + + mac = hwaddr[0]; + mac = (mac << 8) | hwaddr[1]; + mac = (mac << 8) | hwaddr[2]; + mac = (mac << 8) | hwaddr[3]; + mac = (mac << 8) | hwaddr[4]; + mac = (mac << 8) | hwaddr[5]; + return true; +} + +} // namespace + protocol_zt::protocol_zt() { zerotier_network_start(); @@ -346,27 +372,22 @@ bool protocol_zt::is_peer_connected(endpoint &peer) return it != peer_list.end() && it->second.fd != -1; } -bool protocol_zt::is_peer_relayed(const endpoint &peer) const +std::optional protocol_zt::is_peer_relayed(const endpoint &peer) const { - ip6_addr_t address = {}; - IP6_ADDR_PART(&address, 0, peer.addr[0], peer.addr[1], peer.addr[2], peer.addr[3]); - IP6_ADDR_PART(&address, 1, peer.addr[4], peer.addr[5], peer.addr[6], peer.addr[7]); - IP6_ADDR_PART(&address, 2, peer.addr[8], peer.addr[9], peer.addr[10], peer.addr[11]); - IP6_ADDR_PART(&address, 3, peer.addr[12], peer.addr[13], peer.addr[14], peer.addr[15]); - - const u8_t *hwaddr; - if (nd6_get_next_hop_addr_or_queue(netif_default, nullptr, &address, &hwaddr) != ERR_OK) - return true; - - uint64_t mac = hwaddr[0]; - mac = (mac << 8) | hwaddr[1]; - mac = (mac << 8) | hwaddr[2]; - mac = (mac << 8) | hwaddr[3]; - mac = (mac << 8) | hwaddr[4]; - mac = (mac << 8) | hwaddr[5]; + uint64_t mac; + if (!GetMAC(peer, mac)) + return std::nullopt; return zerotier_is_relayed(mac); } +std::optional protocol_zt::get_latency_to(const endpoint &peer) const +{ + uint64_t mac; + if (!GetMAC(peer, mac)) + return std::nullopt; + return zerotier_latency(mac); +} + std::string protocol_zt::make_default_gamename() { std::string ret; diff --git a/Source/dvlnet/protocol_zt.h b/Source/dvlnet/protocol_zt.h index dbe7d0275..623c3a323 100644 --- a/Source/dvlnet/protocol_zt.h +++ b/Source/dvlnet/protocol_zt.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -84,7 +85,8 @@ public: tl::expected network_online(); tl::expected peers_ready(); bool is_peer_connected(endpoint &peer); - bool is_peer_relayed(const endpoint &peer) const; + std::optional is_peer_relayed(const endpoint &peer) const; + std::optional get_latency_to(const endpoint &peer) const; static std::string make_default_gamename(); private: diff --git a/Source/dvlnet/zerotier_native.cpp b/Source/dvlnet/zerotier_native.cpp index 27182f47f..37061e477 100644 --- a/Source/dvlnet/zerotier_native.cpp +++ b/Source/dvlnet/zerotier_native.cpp @@ -211,5 +211,17 @@ bool zerotier_is_relayed(uint64_t mac) return isRelayed; } +int zerotier_latency(uint64_t mac) +{ + int latency = -1; + if (zts_core_lock_obtain() != ZTS_ERR_OK) + return latency; + zts_peer_info_t peerInfo; + if (zts_core_query_peer_info(ZtNetwork, mac, &peerInfo) == ZTS_ERR_OK) + latency = peerInfo.latency; + zts_core_lock_release(); + return latency; +} + } // namespace net } // namespace devilution diff --git a/Source/dvlnet/zerotier_native.h b/Source/dvlnet/zerotier_native.h index 06a2fee30..c84afe3f4 100644 --- a/Source/dvlnet/zerotier_native.h +++ b/Source/dvlnet/zerotier_native.h @@ -9,6 +9,7 @@ bool zerotier_network_ready(); bool zerotier_peers_ready(); void zerotier_network_start(); bool zerotier_is_relayed(uint64_t mac); +int zerotier_latency(uint64_t mac); // NOTE: We have patched our libzt to have the corresponding multicast // MAC hardcoded, since libzt is still missing the proper handling. diff --git a/Source/storm/storm_net.cpp b/Source/storm/storm_net.cpp index 72bf4498b..40862e9c1 100644 --- a/Source/storm/storm_net.cpp +++ b/Source/storm/storm_net.cpp @@ -15,6 +15,7 @@ #include "engine/demomode.h" #include "headless_mode.hpp" #include "menu.h" +#include "multi.h" #include "options.h" #include "utils/stubs.h" #include "utils/utf8.hpp" @@ -238,4 +239,9 @@ bool DvlNet_IsPublicGame() return GameIsPublic; } +DvlNetLatencies DvlNet_GetLatencies(uint8_t playerId) +{ + return dvlnet_inst->get_latencies(playerId); +} + } // namespace devilution diff --git a/Source/storm/storm_net.hpp b/Source/storm/storm_net.hpp index 52e2af529..c4c7b1df3 100644 --- a/Source/storm/storm_net.hpp +++ b/Source/storm/storm_net.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -42,6 +43,12 @@ struct _SNETEVENT { size_t databytes; // native-endian }; +struct DvlNetLatencies { + uint32_t echoLatency; + std::optional providerLatency; + std::optional isRelayed; +}; + #define PS_CONNECTED 0x10000 #define PS_TURN_ARRIVED 0x20000 #define PS_ACTIVE 0x40000 @@ -138,5 +145,6 @@ std::vector DvlNet_GetGamelist(); void DvlNet_SetPassword(std::string pw); void DvlNet_ClearPassword(); bool DvlNet_IsPublicGame(); +DvlNetLatencies DvlNet_GetLatencies(uint8_t playerId); } // namespace devilution