Files
smack-a-brother/src/net.h
T
shweet 37560547d4 Lobby browser, names and passwords, The Good Stuff, smarter queues, and a balance pass
- Lobbies: a name (default "Lobby <code>"), Unlisted or Public, and an optional password. The Multiplayer page
  scrolls: a Browser of public lobbies (name, count, padlock, code, little player previews, Join with a password
  field when locked), Join by code, Create, then Settings. Errors show under whatever caused them. Links take
  ?pass= (and --pass), and Copy Link includes it. Protocol 11.
- Queue areas center on their host along the floor's flat stretch, squeezing their slots to fit, and move to a nearby
  surface when the host's is too short.
- An airblast steals an enemy's flail ball onto its own chain for a while. Flail balls reel themselves back after 15
  seconds. Doubler echoes of the Grimoire teleport move you too.
- The Good Stuff: a new trinket, the inverse of Diazepam (more spread). Spread is now a multiplier, and it widens or
  narrows the Vacuum's cone.
- Balance: Sword basic slash 25% smaller and charged slash half the size, Whip basic lash half the damage and reach,
  Recoil Coil recoil halved, Speedometer no longer speeds you up.
- Fixes: fashions survive coming back from a game, Quit shows only in the sandbox menu, and the chat log keeps above
  the menu's tip.
2026-11-10 23:01:32 -05:00

121 lines
4.0 KiB
C++

#pragma once
#include "protocol.h"
#include <map>
#include <memory>
#include <vector>
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<std::pair<int, int>> 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 <code>"), 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<LobbyListing> listings;
std::string listError;
int selfId = -1;
int hostId = -1;
std::vector<NetMember> members;
// Binary messages from the server, oldest first, for the session to take.
std::vector<std::string> packets;
// Lines for the chat log from the server (like this screen being kicked), oldest first, for the chat to take.
std::vector<NetNotice> notices;
// Members the server said were kicked, with the reason (empty for none given).
std::map<int, std::string> kicks;
private:
struct Impl;
std::unique_ptr<Impl> 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;
};