You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
131 lines
3.3 KiB
131 lines
3.3 KiB
#pragma once |
|
|
|
#include <algorithm> |
|
#include <array> |
|
#include <atomic> |
|
#include <cstdint> |
|
#include <deque> |
|
#include <exception> |
|
#include <optional> |
|
#include <string> |
|
|
|
#include <ankerl/unordered_dense.h> |
|
|
|
#include "dvlnet/frame_queue.h" |
|
#include "dvlnet/packet.h" |
|
#include "utils/log.hpp" |
|
|
|
namespace devilution { |
|
namespace net { |
|
|
|
template <typename... Args> |
|
PacketError ProtocolError(std::string_view fmt, Args &&...args) |
|
{ |
|
auto str = detail::format(fmt, std::forward<Args>(args)...); |
|
return PacketError(str); |
|
} |
|
|
|
class protocol_zt { |
|
public: |
|
class endpoint { |
|
public: |
|
std::array<unsigned char, 16> addr = {}; |
|
|
|
explicit operator bool() const |
|
{ |
|
auto empty = std::array<unsigned char, 16> {}; |
|
return (addr != empty); |
|
} |
|
|
|
bool operator==(const endpoint &rhs) const |
|
{ |
|
return addr == rhs.addr; |
|
} |
|
|
|
bool operator!=(const endpoint &rhs) const |
|
{ |
|
return !(*this == rhs); |
|
} |
|
|
|
bool operator<(const endpoint &rhs) const |
|
{ |
|
return addr < rhs.addr; |
|
} |
|
|
|
buffer_t serialize() const |
|
{ |
|
return buffer_t(addr.begin(), addr.end()); |
|
} |
|
|
|
tl::expected<void, PacketError> unserialize(const buffer_t &buf) |
|
{ |
|
if (buf.size() != 16) { |
|
std::string_view format = "Endpoint deserialization expected 16 bytes, got {}"; |
|
PacketError error = ProtocolError(format, buf.size()); |
|
return tl::make_unexpected(std::move(error)); |
|
} |
|
std::copy(buf.begin(), buf.end(), addr.begin()); |
|
return {}; |
|
} |
|
|
|
void from_string(const std::string &str); |
|
}; |
|
struct EndpointHash { |
|
using is_avalanching = void; |
|
|
|
[[nodiscard]] uint64_t operator()(const endpoint &e) const noexcept |
|
{ |
|
return ankerl::unordered_dense::hash<std::string_view> {}( |
|
std::string_view { reinterpret_cast<const char *>(e.addr.data()), e.addr.size() }); |
|
} |
|
}; |
|
|
|
protocol_zt(); |
|
~protocol_zt(); |
|
void disconnect(const endpoint &peer); |
|
tl::expected<void, PacketError> send(const endpoint &peer, const buffer_t &data); |
|
bool send_oob(const endpoint &peer, const buffer_t &data) const; |
|
bool send_oob_mc(const buffer_t &data) const; |
|
bool recv(endpoint &peer, buffer_t &data); |
|
bool get_disconnected(endpoint &peer); |
|
tl::expected<bool, PacketError> network_online(); |
|
tl::expected<bool, PacketError> peers_ready(); |
|
bool is_peer_connected(endpoint &peer); |
|
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: |
|
static constexpr uint32_t PKTBUF_LEN = 65536; |
|
static constexpr uint16_t default_port = 6112; |
|
|
|
struct peer_state { |
|
int fd = -1; |
|
std::deque<buffer_t> send_queue; |
|
frame_queue recv_queue; |
|
}; |
|
|
|
std::deque<std::pair<endpoint, buffer_t>> oob_recv_queue; |
|
std::deque<endpoint> disconnect_queue; |
|
|
|
ankerl::unordered_dense::map<endpoint, peer_state, EndpointHash> peer_list; |
|
int fd_tcp = -1; |
|
int fd_udp = -1; |
|
|
|
static uint64_t current_ms(); |
|
void close_all(); |
|
|
|
static void set_nonblock(int fd); |
|
static void set_nodelay(int fd); |
|
static void set_reuseaddr(int fd); |
|
|
|
tl::expected<bool, PacketError> send_queued_peer(const endpoint &peer); |
|
bool recv_peer(const endpoint &peer); |
|
bool send_queued_all(); |
|
bool recv_from_peers(); |
|
bool recv_from_udp(); |
|
bool accept_all(); |
|
}; |
|
|
|
} // namespace net |
|
} // namespace devilution
|
|
|