RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
SignalingClient.cpp
Go to the documentation of this file.
1#include "SignalingClient.h"
2#include <rtc/rtc.hpp>
3#include <iostream>
4#include <nlohmann/json.hpp>
5#include "NetworkManager.h"
6#include "NetworkUtilities.h"
7#include "Message.h"
8
9using json = nlohmann::json;
10using Clock = std::chrono::steady_clock;
11
12SignalingClient::SignalingClient(std::weak_ptr<NetworkManager> parent) :
13 network_manager(parent)
14{
15}
16
17// SignalingClient.cpp
18bool SignalingClient::connectUrl(const std::string& url)
19{
20 rtc::WebSocketConfiguration cfg;
21 cfg.pingInterval = std::chrono::milliseconds(1000);
22 cfg.connectionTimeout = std::chrono::milliseconds(0);
23 ws = std::make_shared<rtc::WebSocket>(cfg);
24
25 ws->onOpen([=]()
26 {
27 if (auto nm = network_manager.lock()) {
28 nm->pushStatusToast("Signaling connected", ImGuiToaster::Level::Good);
29 // send AUTH with username + uniqueId from IdentityManager
30 ws->send(msg::makeAuth(nm->getNetworkPassword(),
31 nm->getMyUsername(),
32 nm->getMyUniqueId()).dump());
33 } });
34
35 ws->onClosed([=]()
36 {
37 if (auto nm = network_manager.lock())
38 nm->pushStatusToast("Signaling disconnected", ImGuiToaster::Level::Error); });
39
40 ws->onMessage([=](std::variant<rtc::binary, rtc::string> m)
41 {
42 if (!std::holds_alternative<rtc::string>(m)) return;
43 this->onMessage(std::get<rtc::string>(m)); });
44
45 const std::string norm = NetworkUtilities::normalizeWsUrl(url);
46 try
47 {
48 ws->isClosed() ? ws->open(norm) : (ws->close(), ws->open(norm));
49 }
50 catch (const std::exception& e)
51 {
52 std::cout << "ws->open: " << e.what() << "\n";
53 return false;
54 }
55 return true;
56}
57
58bool SignalingClient::connect(const std::string& ip, unsigned short port)
59{
60 // (same as your version, only keep the AUTH send identical to above)
61 rtc::WebSocketConfiguration cfg;
62 cfg.pingInterval = std::chrono::milliseconds(1000);
63 cfg.connectionTimeout = std::chrono::milliseconds(0);
64 ws = std::make_shared<rtc::WebSocket>(cfg);
65
66 ws->onOpen([=]()
67 {
68 auto nm = network_manager.lock();
69 if (!nm) throw std::runtime_error("[SignalingClient] NetworkManager Inactive");
70 ws->send(msg::makeAuth(nm->getNetworkPassword(),
71 nm->getMyUsername(),
72 nm->getMyUniqueId()).dump()); });
73
74 ws->onMessage([=](std::variant<rtc::binary, rtc::string> m)
75 {
76 if (!std::holds_alternative<rtc::string>(m)) return;
77 this->onMessage(std::get<rtc::string>(m)); });
78
79 const std::string url = NetworkUtilities::normalizeWsUrl(ip, port);
80 try
81 {
82 ws->isClosed() ? ws->open(url) : (ws->close(), ws->open(url));
83 }
84 catch (const std::exception& e)
85 {
86 std::cout << "ws->open: " << e.what() << "\n";
87 return false;
88 }
89 return true;
90}
91
92void SignalingClient::send(const std::string& message)
93{
94 if (ws && !ws->isClosed())
95 {
96 ws->send(message);
97 }
98}
99
100void SignalingClient::onMessage(const std::string& msg)
101{
102 using json = nlohmann::json;
103 json j;
104 try
105 {
106 j = json::parse(msg);
107 }
108 catch (...)
109 {
110 return;
111 }
112 const std::string type = j.value(msg::key::Type, "");
113 if (type.empty())
114 return;
115
116 auto nm = network_manager.lock();
117 if (!nm)
118 throw std::runtime_error("NM expired");
119
121 {
122 if (auto nm = network_manager.lock())
123 {
124 nm->disconectFromPeers();
125 }
126 return;
127 }
128
129 // SignalingClient::onMessage
131 {
132 const std::string target = j.value(std::string(msg::key::Target), "");
133 if (target.empty())
134 return;
135 if (auto nm = network_manager.lock())
136 {
137 nm->removePeer(target); // close PC/DC and erase from map
138 }
139 return;
140 }
141
143 {
145 {
146 // Routing id assigned by the server
147 const std::string newPeerId = j.value(std::string(msg::key::ClientId), "");
148 // --- bind *self* now that peerId is known ---
149 const std::string oldPeerId = nm->getMyPeerId();
150 nm->setMyPeerId(newPeerId);
151 // bind *self* now that peerId is known
152 if (auto idm = nm->getIdentityManager())
153 {
154 if (!oldPeerId.empty() && oldPeerId != newPeerId)
155 idm->erasePeer(oldPeerId); // optional helper to drop stale mapping
156
157 idm->bindPeer(newPeerId, idm->myUniqueId(), idm->myUsername());
158 }
159
160 // GM id now carries GM UNIQUE ID
161 const std::string gmUniqueId = j.value(std::string(msg::key::GmId), "");
162 if (!gmUniqueId.empty())
163 {
164 nm->setGMId(gmUniqueId);
165 }
166
167 // Start offers to already-authed peers
168 if (j.contains(msg::key::Clients) && j[msg::key::Clients].is_array())
169 {
170 for (auto& v : j[msg::key::Clients])
171 {
172 std::string peerId = v.get<std::string>();
173 auto link = nm->ensurePeerLink(peerId);
174 link->createChannels();
175 link->createOffer();
176 }
177 }
178 }
179 return;
180 }
181
182 if (type == msg::signaling::Offer)
183 {
184 const std::string from = j.value(msg::key::From, "");
185 const std::string sdp = j.value(msg::key::Sdp, "");
186 const std::string username = j.value(msg::key::Username, "guest_" + from);
187 const std::string uniqueId = j.value(std::string(msg::key::UniqueId), "");
188 if (from.empty() || sdp.empty() || uniqueId.empty())
189 {
191 "Signaling missing fields (from/sdp/uniqueId). Dropping.");
192 return;
193 }
194
195 auto link = nm->ensurePeerLink(from);
196 nm->upsertPeerIdentityWithUnique(from, uniqueId, username);
197 link->setRemoteDescription(rtc::Description(sdp, std::string(msg::signaling::Offer)));
198 link->createAnswer();
199 return;
200 }
201
202 if (type == msg::signaling::Answer)
203 {
204 const std::string from = j.value(msg::key::From, "");
205 const std::string sdp = j.value(msg::key::Sdp, "");
206 const std::string username = j.value(msg::key::Username, "guest_" + from);
207 const std::string uniqueId = j.value(std::string(msg::key::UniqueId), "");
208 if (from.empty() || sdp.empty() || uniqueId.empty())
209 {
211 "Signaling missing fields (from/sdp/uniqueId). Dropping.");
212 return;
213 }
214
215 auto link = nm->ensurePeerLink(from);
216 nm->upsertPeerIdentityWithUnique(from, uniqueId, username);
217 link->setRemoteDescription(rtc::Description(sdp, std::string(msg::signaling::Answer)));
218 return;
219 }
220
221 if (type == msg::signaling::Candidate)
222 {
223 const std::string from = j.value(msg::key::From, "");
224 const std::string cand = j.value(msg::key::Candidate, "");
225 const std::string mid = j.value(msg::key::SdpMid, "");
226 if (from.empty() || cand.empty())
227 return;
228
229 auto link = nm->ensurePeerLink(from);
230 link->addIceCandidate(rtc::Candidate(cand, mid));
231 return;
232 }
233}
234
std::chrono::steady_clock Clock
nlohmann::json json
static Logger & instance()
Definition Logger.h:39
static std::string normalizeWsUrl(const std::string &hostOrUrl, unsigned short port)
static void safeCloseWebSocket(std::shared_ptr< rtc::WebSocket > &ws)
bool connectUrl(const std::string &url)
std::weak_ptr< NetworkManager > network_manager
bool connect(const std::string &ip, unsigned short port)
SignalingClient(std::weak_ptr< NetworkManager > parent)
void send(const std::string &message)
void onMessage(const std::string &msg)
std::shared_ptr< rtc::WebSocket > ws
constexpr std::string_view Type
Definition Message.h:229
constexpr std::string_view Target
Definition Message.h:239
constexpr std::string_view GmId
Definition Message.h:240
constexpr std::string_view UniqueId
Definition Message.h:248
constexpr std::string_view AuthOk
Definition Message.h:249
constexpr std::string_view From
Definition Message.h:230
constexpr std::string_view Sdp
Definition Message.h:242
constexpr std::string_view SdpMid
Definition Message.h:244
constexpr std::string_view Candidate
Definition Message.h:243
constexpr std::string_view Clients
Definition Message.h:235
constexpr std::string_view Username
Definition Message.h:237
constexpr std::string_view ClientId
Definition Message.h:238
constexpr std::string_view ServerDisconnect
Definition Message.h:281
constexpr std::string_view PeerDisconnect
Definition Message.h:282
constexpr std::string_view Answer
Definition Message.h:273
constexpr std::string_view AuthResponse
Definition Message.h:279
constexpr std::string_view Offer
Definition Message.h:272
constexpr std::string_view Candidate
Definition Message.h:275
constexpr std::string False
Definition Message.h:287
constexpr std::string True
Definition Message.h:288
Definition Message.h:28
Json makeAuth(const std::string &token, const std::string &username, const std::string &uniqueId)
Definition Message.h:495