#pragma once // Thin ENet wrapper (reliable channel + unreliable channel) and a UDP // broadcast beacon for finding servers on the LAN. #include #include #include #include struct _ENetHost; struct _ENetPeer; namespace bs::net { enum Channel : uint8_t { Reliable = 0, Unreliable = 1, kChannelCount = 2 }; struct Event { enum Type { None, Connect, Disconnect, Receive } type = None; uint32_t peer = 0; std::vector data; }; bool initialize(); // once per process (enet_initialize); safe to call repeatedly void shutdown(); class Host { public: Host() = default; Host(const Host&) = delete; Host& operator=(const Host&) = delete; ~Host(); bool listen(uint16_t port, size_t maxPeers); // Starts connecting; a Connect (or Disconnect on failure) event follows. bool connect(const std::string& address, uint16_t port); // Next event; waits up to timeoutMs for the first one. bool poll(Event& ev, uint32_t timeoutMs = 0); void send(uint32_t peer, Channel ch, const std::vector& data); void disconnect(uint32_t peer); void flush(); std::string address(uint32_t peer) const; uint32_t roundTripMs(uint32_t peer) const; bool valid() const { return host_ != nullptr; } private: _ENetHost* host_ = nullptr; uint32_t nextId_ = 1; std::unordered_map peers_; }; struct ServerInfo { std::string address; // ip the reply came from uint16_t port = 0; std::string name; int players = 0; }; // Server side: answers "BSAFARI?" broadcasts on kDiscoveryPort. class Beacon { public: ~Beacon(); bool open(uint16_t discoveryPort); void poll(uint16_t gamePort, const std::string& serverName, int players); private: intptr_t sock_ = -1; }; // Client side: broadcasts a query and collects replies. class Scanner { public: ~Scanner(); bool open(); void ping(uint16_t discoveryPort); void poll(std::vector& found); private: intptr_t sock_ = -1; }; } // namespace bs::net