RunicVTT Open Source Virtual Tabletop for TTRPG using P2P
Loading...
Searching...
No Matches
PeerLink Class Reference

#include <PeerLink.h>

Collaboration diagram for PeerLink:

Public Member Functions

 PeerLink (const std::string &id, std::weak_ptr< NetworkManager > parent)
 
 PeerLink ()
 
void close ()
 
void createChannels ()
 
void createDataChannel (const std::string &label)
 
rtc::Description createOffer ()
 
rtc::Description createAnswer ()
 
void setRemoteDescription (const rtc::Description &desc)
 
void addIceCandidate (const rtc::Candidate &candidate)
 
void send (const std::string &msg)
 
bool sendOn (const std::string &label, const std::vector< uint8_t > &bytes)
 
bool sendOn (const std::string &label, std::string_view text)
 
bool sendGame (const std::vector< uint8_t > &bytes)
 
bool sendChat (const std::vector< uint8_t > &bytes)
 
bool sendNote (const std::vector< uint8_t > &bytes)
 
bool sendMarkerMove (const std::vector< uint8_t > &bytes)
 
void sendChatJson (const std::string &jsonText)
 
void setDisplayName (std::string n)
 
const std::string & displayName () const
 
void attachChannelHandlers (const std::shared_ptr< rtc::DataChannel > &ch, const std::string &label)
 
void attachMarkerMoveChannelHandlers (const std::shared_ptr< rtc::DataChannel > &ch, const std::string &label)
 
bool isDataChannelOpen () const
 
rtc::PeerConnection::State pcState () const
 
const char * pcStateString () const
 
bool isClosedOrFailed () const
 
const char * pcStateToStr (rtc::PeerConnection::State s)
 
bool isConnected () const
 
bool isPcConnectedOnly () const
 
void setOpen (std::string label, bool open)
 
bool allRequiredOpen () const
 
bool bootstrapSent () const
 
void markBootstrapSent ()
 
void markBootstrapReset ()
 
std::shared_ptr< rtc::PeerConnection > getPeerConnection ()
 

Private Member Functions

void setupCallbacks ()
 

Private Attributes

std::string peerId
 
std::string displayName_
 
std::shared_ptr< rtc::PeerConnection > pc
 
std::unordered_map< std::string, std::shared_ptr< rtc::DataChannel > > dcs_
 
std::atomic< bool > closing_ {false}
 
std::weak_ptr< NetworkManagernetwork_manager
 
std::atomic< rtc::PeerConnection::State > lastState_ {rtc::PeerConnection::State::New}
 
std::atomic< double > lastStateAt_ {0.0}
 
std::atomic< bool > remoteDescSet_ {false}
 
std::vector< rtc::Candidate > pendingRemoteCandidates_
 
std::mutex candMx_
 
bool bootstrapSent_ = false
 
std::unordered_map< std::string, bool > dcOpen_
 

Detailed Description

Definition at line 8 of file PeerLink.h.

Constructor & Destructor Documentation

◆ PeerLink() [1/2]

PeerLink::PeerLink ( const std::string & id,
std::weak_ptr< NetworkManager > parent )

Definition at line 7 of file PeerLink.cpp.

7 :
8 peerId(id), network_manager(parent)
9{
10 if (auto nm = network_manager.lock())
11 {
12 auto config = nm->getRTCConfig();
13 config.iceServers.push_back({"stun:stun.l.google.com:19302"}); // Google
14 config.iceServers.push_back({"stun:stun1.l.google.com:19302"}); // Google alt
15 config.iceServers.push_back({"stun:stun.stunprotocol.org:3478"}); // Community server
16 pc = std::make_shared<rtc::PeerConnection>(config);
18 }
19 else
20 {
21 throw std::runtime_error("NetworkManager expired");
22 }
23}
Here is the call graph for this function:

◆ PeerLink() [2/2]

PeerLink::PeerLink ( )

Member Function Documentation

◆ addIceCandidate()

void PeerLink::addIceCandidate ( const rtc::Candidate & candidate)

Definition at line 115 of file PeerLink.cpp.

116{
117 std::lock_guard<std::mutex> lk(candMx_);
118 if (!remoteDescSet_.load(std::memory_order_acquire))
119 {
120 pendingRemoteCandidates_.push_back(candidate);
121 }
122 else
123 {
124 pc->addRemoteCandidate(candidate);
125 }
126}

◆ allRequiredOpen()

bool PeerLink::allRequiredOpen ( ) const

Definition at line 235 of file PeerLink.cpp.

236{
237 // minimal: only require the channel used for snapshots
238 auto it = dcOpen_.find(msg::dc::name::Game);
239 return it != dcOpen_.end() && it->second;
240}
constexpr std::string Game
Definition Message.h:297

◆ attachChannelHandlers()

void PeerLink::attachChannelHandlers ( const std::shared_ptr< rtc::DataChannel > & ch,
const std::string & label )

Definition at line 242 of file PeerLink.cpp.

243{
244 if (!dc)
245 return;
246
247 dc->onOpen([this, id = peerId, label]()
248 {
249 std::cout << "[PeerLink] DC open \"" << label << "\" to " << id << "\n";
250 if (auto nm = network_manager.lock())
251 {
252 msg::NetEvent ev{msg::NetEvent::Type::DcOpen, id, label};
253 nm->events_.push(std::move(ev)); // or nm->notifyDcOpen(id, label);
254 }
255 dcOpen_[label] = true; });
256
257 dc->onClosed([this, id = peerId, label]()
258 {
259 std::cout << "[PeerLink] DC closed \"" << label << "\" to " << id << "\n";
260 dcOpen_[label] = false;
261 bootstrapSent_ = false;
262 if (auto nm = network_manager.lock()) {
264 nm->events_.push(std::move(ev));
265 } });
266
267 dc->onMessage([this, id = peerId, label](rtc::message_variant m)
268 {
269 Logger::instance().log("localtunnel", Logger::Level::Info, "MESSAGE RECEIVED!! FROM: "+label);
270 if (auto nm = network_manager.lock())
271 {
272 if (std::holds_alternative<std::string>(m))
273 {
274 const auto& s = std::get<std::string>(m);
275 std::vector<uint8_t> bytes(s.begin(), s.end());
276 nm->inboundRaw_.push(msg::InboundRaw{peerId, label, std::move(bytes)});
277 }
278 else
279 {
280 const auto& bin = std::get<rtc::binary>(m);
281 std::vector<uint8_t> bytes(bin.size());
282 std::memcpy(bytes.data(), bin.data(), bin.size());
283 nm->inboundRaw_.push(msg::InboundRaw{peerId, label, std::move(bytes)});
284 }
285 } });
286}
static Logger & instance()
Definition Logger.h:39
Here is the caller graph for this function:

◆ attachMarkerMoveChannelHandlers()

void PeerLink::attachMarkerMoveChannelHandlers ( const std::shared_ptr< rtc::DataChannel > & ch,
const std::string & label )

◆ bootstrapSent()

bool PeerLink::bootstrapSent ( ) const
inline

Definition at line 62 of file PeerLink.h.

63 {
64 return bootstrapSent_;
65 }

◆ close()

void PeerLink::close ( )

Definition at line 342 of file PeerLink.cpp.

343{
344 static std::atomic<uint64_t> guardSeq{0};
345 const uint64_t seq = ++guardSeq;
346
347 if (closing_.exchange(true))
348 {
349 Logger::instance().log("main", Logger::Level::Debug, "PeerLink::close() re-entry ignored");
350 return;
351 }
352
353 Logger::instance().log("main", Logger::Level::Debug, "PeerLink::close() begin #" + std::to_string(seq));
354
355 try
356 {
357 for (auto& [label, ch] : dcs_)
358 {
359 if (!ch)
360 continue;
361 ch->onOpen(nullptr);
362 ch->onMessage(nullptr);
363 ch->onBufferedAmountLow(nullptr);
364 ch->onClosed(nullptr);
365 ch->onError(nullptr);
366 }
367 if (pc)
368 {
369 pc->onStateChange(nullptr);
370 pc->onGatheringStateChange(nullptr);
371 pc->onLocalDescription(nullptr);
372 pc->onDataChannel(nullptr);
373 pc->onTrack(nullptr);
374 }
375 }
376 catch (...)
377 {
378 // ignore
379 }
380
381 std::unordered_map<std::string, std::shared_ptr<rtc::DataChannel>> movedDcs;
382 movedDcs.swap(dcs_);
383
384 for (auto& kv : movedDcs)
385 {
387 }
388 movedDcs.clear();
389
391
392 Logger::instance().log("main", Logger::Level::Debug, "PeerLink::close() end #" + std::to_string(seq));
393}
static void safeCloseDataChannel(std::shared_ptr< rtc::DataChannel > &dc)
static void safeClosePeerConnection(std::shared_ptr< rtc::PeerConnection > &pc)
Here is the call graph for this function:

◆ createAnswer()

rtc::Description PeerLink::createAnswer ( )

Definition at line 91 of file PeerLink.cpp.

92{
93 auto answer = pc->createAnswer();
94 rtc::LocalDescriptionInit init;
95 init.icePwd = answer.icePwd();
96 init.iceUfrag = answer.iceUfrag();
97 pc->setLocalDescription(answer.type(), init);
98 return answer; // Send this via signaling
99}

◆ createChannels()

void PeerLink::createChannels ( )

Definition at line 50 of file PeerLink.cpp.

51{
52 if (!pc)
53 return;
54
55 rtc::DataChannelInit init; // default reliable/ordered
56 rtc::DataChannelInit markerMoveInit; //
57 auto reliability = rtc::Reliability{};
58 reliability.unordered = true;
59 reliability.maxPacketLifeTime = std::chrono::milliseconds(500);
60 //reliability.maxRetransmits = 0;
61 markerMoveInit.reliability = reliability;
62
63 // Offerer creates channels; answerer gets them in pc->onDataChannel
64 auto dcGame = pc->createDataChannel(std::string(msg::dc::name::Game), init);
65 dcs_[std::string(msg::dc::name::Game)] = dcGame;
66 attachChannelHandlers(dcGame, std::string(msg::dc::name::Game));
67
68 auto dcChat = pc->createDataChannel(std::string(msg::dc::name::Chat), init);
69 dcs_[std::string(msg::dc::name::Chat)] = dcChat;
70 attachChannelHandlers(dcChat, std::string(msg::dc::name::Chat));
71
72 auto dcNotes = pc->createDataChannel(std::string(msg::dc::name::Notes), init);
73 dcs_[std::string(msg::dc::name::Notes)] = dcNotes;
74 attachChannelHandlers(dcNotes, std::string(msg::dc::name::Notes));
75
76 auto dcMarkerMove = pc->createDataChannel(std::string(msg::dc::name::MarkerMove), markerMoveInit);
77 dcs_[std::string(msg::dc::name::MarkerMove)] = dcMarkerMove;
78 attachChannelHandlers(dcMarkerMove, std::string(msg::dc::name::MarkerMove));
79}
constexpr std::string Chat
Definition Message.h:298
constexpr std::string Notes
Definition Message.h:299
constexpr std::string MarkerMove
Definition Message.h:300
Here is the call graph for this function:

◆ createDataChannel()

void PeerLink::createDataChannel ( const std::string & label)

◆ createOffer()

rtc::Description PeerLink::createOffer ( )

Definition at line 81 of file PeerLink.cpp.

82{
83 auto offer = pc->createOffer();
84 rtc::LocalDescriptionInit init;
85 init.icePwd = offer.icePwd(); // string
86 init.iceUfrag = offer.iceUfrag(); // string
87 pc->setLocalDescription(offer.type(), init);
88 return offer;
89}

◆ displayName()

const std::string & PeerLink::displayName ( ) const

Definition at line 31 of file PeerLink.cpp.

32{
33 return displayName_;
34}

◆ getPeerConnection()

std::shared_ptr< rtc::PeerConnection > PeerLink::getPeerConnection ( )
inline

Definition at line 75 of file PeerLink.h.

76 {
77 return pc;
78 }

◆ isClosedOrFailed()

bool PeerLink::isClosedOrFailed ( ) const

Definition at line 402 of file PeerLink.cpp.

403{
404 if (!pc)
405 return true;
406 auto s = pc->state();
407 return s == rtc::PeerConnection::State::Closed ||
408 s == rtc::PeerConnection::State::Failed;
409}

◆ isConnected()

bool PeerLink::isConnected ( ) const

Definition at line 288 of file PeerLink.cpp.

289{
290 // “usable” = PC connected AND at least Game channel open
291 auto it = dcs_.find(std::string(msg::dc::name::Game));
292 return isPcConnectedOnly() && it != dcs_.end() && it->second && it->second->isOpen();
293}
Here is the call graph for this function:

◆ isDataChannelOpen()

bool PeerLink::isDataChannelOpen ( ) const

Definition at line 295 of file PeerLink.cpp.

296{
297 for (auto& [label, ch] : dcs_)
298 {
299 if (ch && ch->isOpen())
300 return true;
301 }
302 return false;
303}

◆ isPcConnectedOnly()

bool PeerLink::isPcConnectedOnly ( ) const

Definition at line 305 of file PeerLink.cpp.

306{
307 return pc && pc->state() == rtc::PeerConnection::State::Connected;
308}
Here is the caller graph for this function:

◆ markBootstrapReset()

void PeerLink::markBootstrapReset ( )
inline

Definition at line 70 of file PeerLink.h.

71 {
72 bootstrapSent_ = false;
73 }

◆ markBootstrapSent()

void PeerLink::markBootstrapSent ( )
inline

Definition at line 66 of file PeerLink.h.

67 {
68 bootstrapSent_ = true;
69 }

◆ pcState()

rtc::PeerConnection::State PeerLink::pcState ( ) const

Definition at line 395 of file PeerLink.cpp.

396{
397 if (!pc)
398 return rtc::PeerConnection::State::Closed;
399 return pc->state();
400}
Here is the caller graph for this function:

◆ pcStateString()

const char * PeerLink::pcStateString ( ) const

Definition at line 411 of file PeerLink.cpp.

412{
413 auto s = pcState();
414 using S = rtc::PeerConnection::State;
415 switch (s)
416 {
417 case S::New:
418 return "New";
419 case S::Connecting:
420 return "Connecting";
421 case S::Connected:
422 return "Connected";
423 case S::Disconnected:
424 return "Disconnected";
425 case S::Failed:
426 return "Failed";
427 case S::Closed:
428 return "Closed";
429 }
430 return "Unknown";
431}
Here is the call graph for this function:

◆ pcStateToStr()

const char * PeerLink::pcStateToStr ( rtc::PeerConnection::State s)

◆ send()

void PeerLink::send ( const std::string & msg)

◆ sendChat()

bool PeerLink::sendChat ( const std::vector< uint8_t > & bytes)

Definition at line 216 of file PeerLink.cpp.

217{
218 return sendOn(std::string(msg::dc::name::Chat), bytes);
219}
Here is the call graph for this function:

◆ sendChatJson()

void PeerLink::sendChatJson ( const std::string & jsonText)

Definition at line 230 of file PeerLink.cpp.

231{
232 sendOn(msg::dc::name::Chat, jsonText);
233}
Here is the call graph for this function:

◆ sendGame()

bool PeerLink::sendGame ( const std::vector< uint8_t > & bytes)

Definition at line 211 of file PeerLink.cpp.

212{
213 return sendOn(std::string(msg::dc::name::Game), bytes);
214}
Here is the call graph for this function:

◆ sendMarkerMove()

bool PeerLink::sendMarkerMove ( const std::vector< uint8_t > & bytes)

Definition at line 226 of file PeerLink.cpp.

227{
228 return sendOn(std::string(msg::dc::name::MarkerMove), bytes);
229}
Here is the call graph for this function:

◆ sendNote()

bool PeerLink::sendNote ( const std::vector< uint8_t > & bytes)

Definition at line 221 of file PeerLink.cpp.

222{
223 return sendOn(std::string(msg::dc::name::Notes), bytes);
224}
Here is the call graph for this function:

◆ sendOn() [1/2]

bool PeerLink::sendOn ( const std::string & label,
const std::vector< uint8_t > & bytes )

Definition at line 187 of file PeerLink.cpp.

188{
189 auto it = dcs_.find(label);
190 if (it == dcs_.end() || !it->second)
191 return false;
192 auto& ch = it->second;
193 if (!ch->isOpen())
194 return false;
195
197 //if (ch->bufferedAmount() > kMaxBufferedBytes)
198 //{
199 // // You can queue locally instead of dropping, if you want
200 // return false;
201 //}
202 rtc::binary b;
203 b.resize(bytes.size());
204 if (!bytes.empty())
205 {
206 std::memcpy(b.data(), bytes.data(), bytes.size()); // std::byte is trivially copyable
207 }
208 ch->send(b); // libdatachannel handles SCTP fragmentation
209 return true;
210}
Here is the caller graph for this function:

◆ sendOn() [2/2]

bool PeerLink::sendOn ( const std::string & label,
std::string_view text )

Definition at line 171 of file PeerLink.cpp.

172{
173 auto it = dcs_.find(label);
174 if (it == dcs_.end() || !it->second)
175 return false;
176 auto& ch = it->second;
177 if (!ch->isOpen())
178 return false;
179
180 // optional backpressure guard
181 // if (ch->bufferedAmount() > kMaxBufferedBytes) return false;
182
183 ch->send(std::string(text)); // TEXT frame over DC
184 return true;
185}

◆ setDisplayName()

void PeerLink::setDisplayName ( std::string n)

Definition at line 26 of file PeerLink.cpp.

27{
28 displayName_ = std::move(n);
29}

◆ setOpen()

void PeerLink::setOpen ( std::string label,
bool open )
inline

Definition at line 56 of file PeerLink.h.

57 {
58 dcOpen_[label] = open;
59 }

◆ setRemoteDescription()

void PeerLink::setRemoteDescription ( const rtc::Description & desc)

Definition at line 101 of file PeerLink.cpp.

102{
103 pc->setRemoteDescription(desc);
104 {
105 std::lock_guard<std::mutex> lk(candMx_);
106 remoteDescSet_.store(true, std::memory_order_release);
107 for (auto& c : pendingRemoteCandidates_)
108 {
109 pc->addRemoteCandidate(c);
110 }
112 }
113}

◆ setupCallbacks()

void PeerLink::setupCallbacks ( )
private

Definition at line 128 of file PeerLink.cpp.

129{
130 pc->onStateChange([this](rtc::PeerConnection::State s)
131 {
132 lastState_ = s;
133 lastStateAt_ = std::chrono::seconds().count();
134 std::cout << "[PeerLink] State(" << peerId << "): " << (int)s << "at " << lastStateAt_ << "\n";
135
136 if (s == rtc::PeerConnection::State::Closed || s == rtc::PeerConnection::State::Failed) {
137 if (auto nm = network_manager.lock()) {
139 nm->events_.push(std::move(ev));
140 }
141 }
142 if (s == rtc::PeerConnection::State::Connected) {
143 if (auto nm = network_manager.lock()) {
145 nm->events_.push(std::move(ev));
146 }
147 } });
148
149 pc->onLocalDescription([wk = network_manager, id = peerId](rtc::Description desc)
150 {
151 if (auto nm = wk.lock()) nm->onPeerLocalDescription(id, desc); });
152
153 pc->onLocalCandidate([wk = network_manager, id = peerId](rtc::Candidate cand)
154 {
155 if (auto nm = wk.lock()) nm->onPeerLocalCandidate(id, cand); });
156
157 pc->onDataChannel([this](std::shared_ptr<rtc::DataChannel> ch)
158 {
159 const std::string label = ch->label();
160 dcs_[label] = ch;
161 attachChannelHandlers(ch, label);
162 std::cout << "[PeerLink] Received DC \"" << label << "\" from " << peerId << "\n"; });
163}
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ bootstrapSent_

bool PeerLink::bootstrapSent_ = false
private

Definition at line 96 of file PeerLink.h.

◆ candMx_

std::mutex PeerLink::candMx_
private

Definition at line 94 of file PeerLink.h.

◆ closing_

std::atomic<bool> PeerLink::closing_ {false}
private

Definition at line 86 of file PeerLink.h.

86{false};

◆ dcOpen_

std::unordered_map<std::string, bool> PeerLink::dcOpen_
private

Definition at line 97 of file PeerLink.h.

◆ dcs_

std::unordered_map<std::string, std::shared_ptr<rtc::DataChannel> > PeerLink::dcs_
private

Definition at line 85 of file PeerLink.h.

◆ displayName_

std::string PeerLink::displayName_
private

Definition at line 82 of file PeerLink.h.

◆ lastState_

std::atomic<rtc::PeerConnection::State> PeerLink::lastState_ {rtc::PeerConnection::State::New}
private

Definition at line 89 of file PeerLink.h.

89{rtc::PeerConnection::State::New};

◆ lastStateAt_

std::atomic<double> PeerLink::lastStateAt_ {0.0}
private

Definition at line 90 of file PeerLink.h.

90{0.0};

◆ network_manager

std::weak_ptr<NetworkManager> PeerLink::network_manager
private

Definition at line 87 of file PeerLink.h.

◆ pc

std::shared_ptr<rtc::PeerConnection> PeerLink::pc
private

Definition at line 83 of file PeerLink.h.

◆ peerId

std::string PeerLink::peerId
private

Definition at line 81 of file PeerLink.h.

◆ pendingRemoteCandidates_

std::vector<rtc::Candidate> PeerLink::pendingRemoteCandidates_
private

Definition at line 93 of file PeerLink.h.

◆ remoteDescSet_

std::atomic<bool> PeerLink::remoteDescSet_ {false}
private

Definition at line 92 of file PeerLink.h.

92{false};

The documentation for this class was generated from the following files: