Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions Source/control.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#include <array>
#include <cstddef>
#include <cstdint>
#include <memory>

Check warning on line 12 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:12:1 [misc-include-cleaner]

included header memory is not used directly
#include <optional>
#include <string>

Expand All @@ -19,20 +19,20 @@
#include <SDL3/SDL_keycode.h>
#include <SDL3/SDL_rect.h>
#else
#include <SDL.h>

Check warning on line 22 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:22:1 [misc-include-cleaner]

included header SDL.h is not used directly

#ifdef USE_SDL1
#include "utils/sdl2_to_1_2_backports.h"
#endif
#endif

#include <fmt/format.h>

Check warning on line 29 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:29:1 [misc-include-cleaner]

included header format.h is not used directly

#include "DiabloUI/text_input.hpp"
#include "automap.h"
#include "controls/control_mode.hpp"
#include "controls/modifier_hints.h"
#include "controls/plrctrls.h"

Check warning on line 35 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:35:1 [misc-include-cleaner]

included header plrctrls.h is not used directly
#include "cursor.h"
#include "diablo_msg.hpp"
#include "engine/backbuffer_state.hpp"
Expand All @@ -48,9 +48,9 @@
#include "inv_iterators.hpp"
#include "levels/setmaps.h"
#include "levels/trigs.h"
#include "lighting.h"

Check warning on line 51 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:51:1 [misc-include-cleaner]

included header lighting.h is not used directly
#include "minitext.h"
#include "missiles.h"

Check warning on line 53 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:53:1 [misc-include-cleaner]

included header missiles.h is not used directly
#include "options.h"
#include "panels/charpanel.hpp"
#include "panels/console.hpp"
Expand All @@ -60,21 +60,22 @@
#include "panels/spell_icons.hpp"
#include "panels/spell_list.hpp"
#include "pfile.h"
#include "playerdat.hpp"

Check warning on line 63 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:63:1 [misc-include-cleaner]

included header playerdat.hpp is not used directly
#include "qol/stash.h"
#include "qol/xpbar.h"
#include "quick_messages.hpp"
#include "stores.h"
#include "storm/storm_net.hpp"
#include "towners.h"
#include "utils/algorithm/container.hpp"
#include "utils/format_int.hpp"
#include "utils/language.h"
#include "utils/log.hpp"

Check warning on line 73 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:73:1 [misc-include-cleaner]

included header log.hpp is not used directly
#include "utils/parse_int.hpp"
#include "utils/screen_reader.hpp"
#include "utils/sdl_compat.h"
#include "utils/sdl_geometry.h"
#include "utils/sdl_ptrs.h"

Check warning on line 78 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:78:1 [misc-include-cleaner]

included header sdl_ptrs.h is not used directly
#include "utils/status_macros.hpp"
#include "utils/str_case.hpp"
#include "utils/str_cat.hpp"
Expand All @@ -99,7 +100,7 @@
bool CharPanelButton[4];
bool LevelButtonDown;
bool CharPanelButtonActive;
UiFlags InfoColor;

Check warning on line 103 in Source/control.cpp

View workflow job for this annotation

GitHub Actions / tidy-check

Source/control.cpp:103:1 [misc-include-cleaner]

no header providing "devilution::UiFlags" is directly included
int SpellbookTab;
bool ChatFlag;
bool SpellbookFlag;
Expand Down Expand Up @@ -810,12 +811,40 @@
"Storybook: ", DungeonSeeds[16]);
}

std::string TextCmdPing(const std::string_view parameter)
{
std::string ret;
const std::string param = AsciiStrToLower(parameter);
auto it = c_find_if(Players, [&param](const Player &player) {
return AsciiStrToLower(player._pName) == param;
});
if (it == Players.end()) {
it = c_find_if(Players, [&param](const Player &player) {
return AsciiStrToLower(player._pName).find(param) != std::string::npos;
});
}
if (it == Players.end()) {
StrAppend(ret, _("No players found with such a name"));
return ret;
}

Player &player = *it;
DvlNetLatencies latencies = DvlNet_GetLatencies(player.getId());
StrAppend(ret, "Echo latency: ", latencies.echoLatency, " ms");
if (latencies.providerLatency != std::nullopt)
StrAppend(ret, "\nProvider latency: ", *latencies.providerLatency, " ms");
if (latencies.isRelayed != std::nullopt && *latencies.isRelayed)
StrAppend(ret, " (relayed)");
return ret;
}

std::vector<TextCmdItem> TextCmdList = {
{ "/help", N_("Prints help overview or help for a specific command."), N_("[command]"), &TextCmdHelp },
{ "/arena", N_("Enter a PvP Arena."), N_("<arena-number>"), &TextCmdArena },
{ "/arenapot", N_("Gives Arena Potions."), N_("<number>"), &TextCmdArenaPot },
{ "/inspect", N_("Inspects stats and equipment of another player."), N_("<player name>"), &TextCmdInspect },
{ "/seedinfo", N_("Show seed infos for current level."), "", &TextCmdLevelSeed },
{ "/ping", N_("Show latency statistics for another player."), N_("<player name>"), &TextCmdPing },
};

bool CheckChatCommand(const std::string_view text)
Expand Down
5 changes: 5 additions & 0 deletions Source/dvlnet/abstract_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,11 @@ class abstract_net {
return std::vector<GameInfo>();
}

virtual DvlNetLatencies get_latencies(uint8_t playerid)
{
return {};
}

static std::unique_ptr<abstract_net> MakeNet(provider_t provider);
};

Expand Down
14 changes: 14 additions & 0 deletions Source/dvlnet/base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ void base::clear_password()
pktfty = std::make_unique<packet_factory>();
}

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<event_type>(ev.eventid)];
Expand Down Expand Up @@ -218,6 +226,12 @@ tl::expected<void, PacketError> base::RecvLocal(packet &pkt)

bool base::SNetReceiveMessage(uint8_t *sender, void **data, size_t *size)
{
uint32_t now = SDL_GetTicks();
if (now == 0) now++;
if (lastEchoTime == 0 || now - lastEchoTime > 5000) {
for (plr_t i = 0; i < Players.size(); i++)
SendEchoRequest(i);
}
poll();
if (message_queue.empty())
return false;
Expand Down
3 changes: 3 additions & 0 deletions Source/dvlnet/base.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ class base : public abstract_net {
void setup_password(std::string pw) override;
void clear_password() override;

DvlNetLatencies get_latencies(uint8_t playerid) override;

~base() override = default;

protected:
Expand Down Expand Up @@ -88,6 +90,7 @@ class base : public abstract_net {
private:
std::array<PlayerState, MAX_PLRS> playerStateTable_;
bool awaitingSequenceNumber_ = true;
uint32_t lastEchoTime = 0;

plr_t GetOwner();
bool AllTurnsArrived();
Expand Down
15 changes: 13 additions & 2 deletions Source/dvlnet/base_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class base_protocol : public base {
bool send_info_request() override;
void clear_gamelist() override;
std::vector<GameInfo> get_gamelist() override;
DvlNetLatencies get_latencies(uint8_t playerid) override;

~base_protocol() override = default;

Expand Down Expand Up @@ -240,7 +241,7 @@ tl::expected<void, PacketError> base_protocol<P>::InitiateHandshake(plr_t player
// It will cause problems if both peers attempt to initiate the handshake simultaneously.
// If the connection is already open, it should be safe to initiate from either end.
// If not, only the player with the smaller player number should initiate the handshake.
if (plr_self < player || proto.is_peer_connected(peer.endpoint))
if (peer.endpoint && (plr_self < player || proto.is_peer_connected(peer.endpoint)))
return SendEchoRequest(player);

return {};
Expand All @@ -260,7 +261,7 @@ tl::expected<void, PacketError> base_protocol<P>::send(packet &pkt)
}
if (destination >= MAX_PLRS)
return tl::make_unexpected("Invalid player ID");
if (destination == MyPlayerId)
if (destination == plr_self)
return {};
return SendTo(destination, pkt);
}
Expand Down Expand Up @@ -557,6 +558,16 @@ std::vector<GameInfo> base_protocol<P>::get_gamelist()
return ret;
}

template <class P>
DvlNetLatencies base_protocol<P>::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 <class P>
bool base_protocol<P>::SNetLeaveGame(int type)
{
Expand Down
5 changes: 5 additions & 0 deletions Source/dvlnet/cdwrap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions Source/dvlnet/cdwrap.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ class cdwrap : public abstract_net {
std::vector<GameInfo> get_gamelist() override;
void setup_password(std::string pw) override;
void clear_password() override;
DvlNetLatencies get_latencies(uint8_t playerid) override;

virtual ~cdwrap() = default;
};
Expand Down
55 changes: 38 additions & 17 deletions Source/dvlnet/protocol_zt.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "dvlnet/protocol_zt.h"

#include <optional>
#include <random>

#ifdef USE_SDL3
Expand All @@ -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();
Expand Down Expand Up @@ -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<bool> 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<int> 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;
Expand Down
4 changes: 3 additions & 1 deletion Source/dvlnet/protocol_zt.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstdint>
#include <deque>
#include <exception>
#include <optional>
#include <string>

#include <ankerl/unordered_dense.h>
Expand Down Expand Up @@ -84,7 +85,8 @@ class protocol_zt {
tl::expected<bool, PacketError> network_online();
tl::expected<bool, PacketError> peers_ready();
bool is_peer_connected(endpoint &peer);
bool is_peer_relayed(const endpoint &peer) const;
std::optional<bool> is_peer_relayed(const endpoint &peer) const;
std::optional<int> get_latency_to(const endpoint &peer) const;
static std::string make_default_gamename();

private:
Expand Down
12 changes: 12 additions & 0 deletions Source/dvlnet/zerotier_native.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 1 addition & 0 deletions Source/dvlnet/zerotier_native.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions Source/storm/storm_net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -238,4 +239,9 @@ bool DvlNet_IsPublicGame()
return GameIsPublic;
}

DvlNetLatencies DvlNet_GetLatencies(uint8_t playerId)
{
return dvlnet_inst->get_latencies(playerId);
}

} // namespace devilution
8 changes: 8 additions & 0 deletions Source/storm/storm_net.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <cstddef>
#include <cstdint>
#include <optional>
#include <string>
#include <vector>

Expand Down Expand Up @@ -42,6 +43,12 @@ struct _SNETEVENT {
size_t databytes; // native-endian
};

struct DvlNetLatencies {
uint32_t echoLatency;
std::optional<int> providerLatency;
std::optional<bool> isRelayed;
};

#define PS_CONNECTED 0x10000
#define PS_TURN_ARRIVED 0x20000
#define PS_ACTIVE 0x40000
Expand Down Expand Up @@ -138,5 +145,6 @@ std::vector<GameInfo> DvlNet_GetGamelist();
void DvlNet_SetPassword(std::string pw);
void DvlNet_ClearPassword();
bool DvlNet_IsPublicGame();
DvlNetLatencies DvlNet_GetLatencies(uint8_t playerId);

} // namespace devilution
Loading