#pragma once #include "protocol.h" #include #include #include struct Game; enum NetState { NET_OFFLINE, NET_CONNECTING, NET_JOINING, NET_LOBBY }; // A line for the chat log from the server, and the member it's about (-1 for none), in whose color it shows. struct NetNotice { std::string text; int member = -1; }; // A public lobby as the server lists it: its code and name, whether it needs a password, and each player's color // (-1 while unassigned) and fashion, for little previews. struct LobbyListing { std::string code; std::string name; std::vector> players; bool isLocked = false; }; // Whether the lobby list shows a lobby, or only its code or link finds it. enum LobbyVisibility { LOBBY_UNLISTED, LOBBY_PUBLIC }; // What a new lobby is made with: its name (empty for "Lobby "), its password (empty for none), and who can find // it (see LobbyVisibility). struct LobbyOptions { char name[LOBBY_NAME_MAX + 1] = {}; char password[LOBBY_PASSWORD_MAX + 1] = {}; int visibility = LOBBY_UNLISTED; }; // Someone in the lobby (this screen's player too): their server id and name, and their connection's round trip to the // server in milliseconds (-1 while it isn't known: this screen only measures its own). struct NetMember { int id = -1; std::string name; float ping = -1; }; // Online play: a WebSocket to the server (see protocol.h) to make or join a lobby, and through it a WebRTC connection // to the server to play over, with two data channels: a reliable, ordered one for everything that mustn't go missing, // and a lossy one for controls and frames, which are sent again until they're confirmed, so a lost one is never waited // on. Until both are open (or where they never open, with UDP blocked), the game's messages go over the socket. // The network calls back on its own threads (on desktop), so everything it reports is queued and handled in update, // once a frame on the main thread. Members are in the order they joined, so the first hosts. Error holds why the last // request failed or the connection was lost, until the next request. class Net { public: Net(); ~Net(); void lobby_create(Game& game, LobbyOptions& options); void lobbies_request(Game& game); void lobby_join(Game& game, const std::string& code, const std::string& password); void lobby_leave(); void update(); void shutdown(); void heartbeat(); void send(const std::string& bytes, Delivery delivery); void kick_send(int member, const std::string& reason); void host_send(int member); bool is_linked(); NetState state = NET_OFFLINE; std::string code; std::string error; // The lobby's name, and the password it was made or joined with (for its link). std::string lobbyName; std::string password; // The public lobbies, as the server last listed them, and why the last listing failed (empty once one comes). std::vector listings; std::string listError; int selfId = -1; int hostId = -1; std::vector members; // Binary messages from the server, oldest first, for the session to take. std::vector packets; // Lines for the chat log from the server (like this screen being kicked), oldest first, for the chat to take. std::vector notices; // Members the server said were kicked, with the reason (empty for none given). std::map kicks; private: struct Impl; std::unique_ptr impl; void request_send(Game& game, const std::string& request, NetState waiting); void send_now(const std::string& bytes, Delivery delivery); void ping_send(); void message_handle(const std::string& text); void link_open(); void signal_handle(const std::string& payload); NetMember* member_get(int id); void host_announce(int previous); std::string& failure_get(); // Whether the request in flight is only asking for the lobby list, so its failures go to listError. bool isListing = false; };