Play over WebRTC to the server

The server answers each player's WebRTC offer (signaled over the socket) with a
connection of its own: frames go on a lossy channel, everything else on a
reliable one, and the socket carries them until both open or where UDP is
blocked. The client's Net now holds one link to the server instead of one per
player. WebRTC takes UDP 47810-47899 by default, set on the command line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shweetandClaude Opus 5.5 committed 2026-11-08 02:09:21 -05:00
1 parent 655332778c
commit dadfff189d
5 files changed
+228 -195

No files matched your search

+14 -24
View File
@@ -15,27 +15,19 @@ enum NetState
NET_LOBBY
};
// A binary message from another member, on the reliable channel (in order, never lost) or the inputs one (either may
// fail).
struct NetPacket
{
int from = -1;
bool isReliable = true;
std::string bytes;
};
// Someone in the lobby (this screen's player too): their server id and name, whether a direct connection to them is
// open, and the round trip over it in milliseconds (-1 until measured). This screen's own member is always connected.
// 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;
bool isConnected = false;
float ping = -1;
};
// Online play: a WebSocket to the server (see protocol.h) to make or join a lobby and pass signaling, and a
// WebRTC data channel to each other member, connected directly. A newcomer connects out to everyone already there.
// 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.
@@ -51,9 +43,9 @@ public:
void update();
void shutdown();
void heartbeat();
void send(int to, const std::string& bytes, bool isReliable);
void send(const std::string& bytes, bool isReliable);
void kick_send(int member, const std::string& reason);
bool is_connected_all();
bool is_linked();
NetState state = NET_OFFLINE;
std::string code;
@@ -61,8 +53,8 @@ public:
int selfId = -1;
int hostId = -1;
std::vector<NetMember> members;
// Binary messages from other members, oldest first, for the session to take.
std::vector<NetPacket> packets;
// 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<std::string> notices;
// Members the server said were kicked, with the reason (empty for none given).
@@ -73,12 +65,10 @@ private:
std::unique_ptr<Impl> impl;
void request_send(Game& game, const std::string& request);
void send_now(int to, const std::string& bytes, bool isReliable);
void text_send(int to, const std::string& text);
void text_send_now(int to, const std::string& text);
void send_now(const std::string& bytes, bool isReliable);
void ping_send();
void message_handle(const std::string& text);
void peer_open(int id, bool isOfferer);
void peer_close(int id);
void signal_handle(int from, const std::string& payload);
void link_open();
void signal_handle(const std::string& payload);
NetMember* member_get(int id);
};