diff --git a/src/PeerManager.ts b/src/PeerManager.ts index 941c6d0..301e0e9 100644 --- a/src/PeerManager.ts +++ b/src/PeerManager.ts @@ -27,13 +27,13 @@ export class PeerManager { RPC_remote: Map = new Map(); rpc: { [key: string]: Function } = {}; isBootstrapPeer: boolean = false; - bootstrapPeerConnection: PeerConnection | null = null; + bootstrapPeerConnections: Map | null = null; sessionID = generateID(); userID: string; peerID: PeerID; websocket: WebSocket | null = null; - bootstrapPeerID: string | null = null; + bootstrapPeerIDs: Set | null = null; connectPromiseCallbacks: { resolve: Function, reject: Function } | null = null; connectPromise: Promise | null = null; @@ -42,7 +42,7 @@ export class PeerManager { eventListeners: Map = new Map(); reconnectPeriod: number = 10; messageSuperlog = false; - watchdogInterval: ReturnType |null = null; + watchdogInterval: ReturnType | null = null; reconnectTimer: number | null = null; peerStateSuperlog: boolean = true; @@ -118,11 +118,11 @@ export class PeerManager { if (message.type === "hello2") { - if (!this.isBootstrapPeer) { - this.bootstrapPeerID = message.bootstrapPeers[0]; + if (!this.isBootstrapPeer && Array.isArray(message?.bootstrapPeers)) { + this.bootstrapPeerIDs = new Set(message.bootstrapPeers); } - this.onHello2Received(this.bootstrapPeerID as string); + this.onHello2Received(this.bootstrapPeerIDs); } if (message.type === "peer_message") { @@ -175,21 +175,41 @@ export class PeerManager { return newPeer; } - async onHello2Received(bootstrapPeerID: string) { + async onHello2Received(bootstrapPeerIDs: Set | null) { if (this.isBootstrapPeer) { this.connectPromiseCallbacks?.resolve(); return; } - if (!bootstrapPeerID) { - console.log.apply(null, log("Didn't get bootstrap peer, waiting 10 seconds...")); + if (!bootstrapPeerIDs) { + console.log.apply(null, log("Didn't get any bootstrap peer, waiting 10 seconds...")); // let callSendHello2OnTimeout = () => { console.log(this, "jajajajaj");this.sendHello2() }; // setTimeout(callSendHello2OnTimeout, 5_000); return; } - this.bootstrapPeerConnection = await this.connectToPeer(bootstrapPeerID); + let bootstrapPeerConnectionPromises = []; + + for (let peerID of bootstrapPeerIDs.keys()) { + // this.bootstrapPeerConnection = await this.connectToPeer(peerID); + + + let bootstrapPeerConnectionPromise = new Promise( async (resolve, reject)=>{ + let peerConnection = await this.connectToPeer(peerID); + if (!peerConnection) { + reject(peerConnection); + } + this.bootstrapPeerConnections?.set(peerID, peerConnection); + resolve(peerConnection); + }) + + + bootstrapPeerConnectionPromises.push(bootstrapPeerConnectionPromise); + } + + await Promise.race(bootstrapPeerConnectionPromises); + this.connectPromiseCallbacks?.resolve(); } @@ -321,7 +341,7 @@ export class PeerManager { let output = `Current status:` + "\n" + `[${logID(this.peerID)}]${this.getPeername(this.peerID)}[local]` + "\n"; for (let [peerID, peer] of this.peers) { - output += `[${logID(peerID)}]${peer.rtcPeer?.connectionState}:${this.getPeername(peerID)}${(peerID === this.bootstrapPeerID) ? "[Bootstrap]" : ""}` + "\n"; + output += `[${logID(peerID)}]${peer.rtcPeer?.connectionState}:${this.getPeername(peerID)}${this.bootstrapPeerIDs?.has(peerID) ? "[Bootstrap]" : ""}` + "\n"; } output += `numActivePeers: ${numActive}` + "\n"; @@ -409,9 +429,9 @@ export class PeerManager { // We should disconnect from the websocket once we connect to our intial peers. // If we have no peer connections, try to connect. If connection fails, start a timer to reconnect. - if (peerID === this.bootstrapPeerID) { - this.bootstrapPeerID = null; - this.bootstrapPeerConnection = null; + if (this.bootstrapPeerIDs?.has(peerID)) { + this.bootstrapPeerIDs.delete(peerID); + this.bootstrapPeerConnections?.delete(peerID); } this.peerStateSuperlog && console.log.apply(null, log(`PeerManager: disconnected from peer ${peerID}`)); @@ -501,10 +521,11 @@ class PeerConnection { private ignoreOffer: boolean = false; private isSettingRemoteAnswerPending: boolean = false; private polite = true; - private webRTCSuperlog = true; + private webRTCSuperlog = false; private dataChannelSuperlog = false; private chunkSize = (16 * 1024) - 100; messageSuperlog: boolean = false; + sendQueueSuperLog: boolean = false; rpcSuperlog: boolean = false; pendingRPCs: Map< string, @@ -522,10 +543,10 @@ class PeerConnection { { urls: "stun:ddln.app" }, // { urls: "turn:ddln.app", username: "a", credential: "b" }, { urls: "stun:stun.l.google.com" }, // keeping this for now as my STUN server is not returning ipv6 - { urls: "stun:stun1.l.google.com" }, - { urls: "stun:stun2.l.google.com" }, - { urls: "stun:stun3.l.google.com" }, - { urls: "stun:stun4.l.google.com" }, + { urls: "stun:stun1.l.google.com" }, + { urls: "stun:stun2.l.google.com" }, + { urls: "stun:stun3.l.google.com" }, + { urls: "stun:stun4.l.google.com" }, ], }; // longMessageQueue: string[] = []; @@ -565,7 +586,7 @@ class PeerConnection { this.send({ type: "hello datachannel", from: this.peerManager.peerID, to: this.remotePeerID }); // this.dataChannel?.send(`{typeHello datachannel from: ${this.peerManager.peerID}`); - console.log.apply(null, log([...this.peerManager.peers.keys()])); + // console.log.apply(null, log([...this.peerManager.peers.keys()])); if (this.peerManager.isBootstrapPeer) { this.send({ type: 'initial_peers', from: this.peerManager.peerID, peers: [...this.peerManager.peers.keys()].filter(entry => entry !== this.remotePeerID) }); @@ -588,7 +609,7 @@ class PeerConnection { this.peerManager.disconnectFromPeer(this.remotePeerID); } - this.dataChannel.onerror = (e: RTCErrorEvent) => { + this.dataChannel.onerror = (e: RTCErrorEvent) => { this.dataChannelSuperlog && console.log.apply(null, log(`datachannel from peer ${this.remotePeerID} error:`, e.error)); } } @@ -656,7 +677,7 @@ class PeerConnection { } - this.rtcPeer.oniceconnectionstatechange = (event:Event) => { + this.rtcPeer.oniceconnectionstatechange = (event: Event) => { this.webRTCSuperlog && console.log.apply(null, log("oniceconnectionstatechange:", this.rtcPeer?.iceConnectionState)); } @@ -776,8 +797,11 @@ class PeerConnection { throw new Error("Send called but datachannel is null"); } + // this.sendQueueSuperLog && console.log.apply(null, log(`[${logID(this.remotePeerID)}]<-datachannel[${logID(this.peerManager.peerID)}]: bufferedAmount ${this.dataChannel.bufferedAmount}`)); + while (this.dataChannel.bufferedAmount >= 8 * 1024 * 1024) { + this.sendQueueSuperLog && console.log.apply(null, log(`[${logID(this.remotePeerID)}]<-datachannel[${logID(this.peerManager.peerID)}]: send buffer full, waiting 1 second`)); await new Promise((resolve, reject) => { setTimeout(() => resolve(), 1000); }) diff --git a/static/PeerManager.js b/static/PeerManager.js index 87b6ade..07a9887 100644 --- a/static/PeerManager.js +++ b/static/PeerManager.js @@ -60,10 +60,10 @@ export class PeerManager { } this.messageSuperlog && console.log.apply(null, log("->signaler:", message)); if (message.type === "hello2") { - if (!this.isBootstrapPeer) { - this.bootstrapPeerID = message.bootstrapPeers[0]; + if (!this.isBootstrapPeer && Array.isArray(message?.bootstrapPeers)) { + this.bootstrapPeerIDs = new Set(message.bootstrapPeers); } - this.onHello2Received(this.bootstrapPeerID); + this.onHello2Received(this.bootstrapPeerIDs); } if (message.type === "peer_message") { let peerConnection = this.peers.get(message.from); @@ -104,18 +104,31 @@ export class PeerManager { this.onPeerConnected(newPeer.remotePeerID); return newPeer; } - async onHello2Received(bootstrapPeerID) { + async onHello2Received(bootstrapPeerIDs) { if (this.isBootstrapPeer) { this.connectPromiseCallbacks?.resolve(); return; } - if (!bootstrapPeerID) { - console.log.apply(null, log("Didn't get bootstrap peer, waiting 10 seconds...")); + if (!bootstrapPeerIDs) { + console.log.apply(null, log("Didn't get any bootstrap peer, waiting 10 seconds...")); // let callSendHello2OnTimeout = () => { console.log(this, "jajajajaj");this.sendHello2() }; // setTimeout(callSendHello2OnTimeout, 5_000); return; } - this.bootstrapPeerConnection = await this.connectToPeer(bootstrapPeerID); + let bootstrapPeerConnectionPromises = []; + for (let peerID of bootstrapPeerIDs.keys()) { + // this.bootstrapPeerConnection = await this.connectToPeer(peerID); + let bootstrapPeerConnectionPromise = new Promise(async (resolve, reject) => { + let peerConnection = await this.connectToPeer(peerID); + if (!peerConnection) { + reject(peerConnection); + } + this.bootstrapPeerConnections?.set(peerID, peerConnection); + resolve(peerConnection); + }); + bootstrapPeerConnectionPromises.push(bootstrapPeerConnectionPromise); + } + await Promise.race(bootstrapPeerConnectionPromises); this.connectPromiseCallbacks?.resolve(); } sendHello2() { @@ -152,10 +165,10 @@ export class PeerManager { this.RPC_remote = new Map(); this.rpc = {}; this.isBootstrapPeer = false; - this.bootstrapPeerConnection = null; + this.bootstrapPeerConnections = null; this.sessionID = generateID(); this.websocket = null; - this.bootstrapPeerID = null; + this.bootstrapPeerIDs = null; this.connectPromiseCallbacks = null; this.connectPromise = null; this.pingPeers = []; @@ -163,7 +176,7 @@ export class PeerManager { this.eventListeners = new Map(); this.reconnectPeriod = 10; this.messageSuperlog = false; - this.watchdogInterval = 0; + this.watchdogInterval = null; this.reconnectTimer = null; this.peerStateSuperlog = true; // async watchdog() { @@ -243,7 +256,7 @@ export class PeerManager { } let output = `Current status:` + "\n" + `[${logID(this.peerID)}]${this.getPeername(this.peerID)}[local]` + "\n"; for (let [peerID, peer] of this.peers) { - output += `[${logID(peerID)}]${peer.rtcPeer?.connectionState}:${this.getPeername(peerID)}${(peerID === this.bootstrapPeerID) ? "[Bootstrap]" : ""}` + "\n"; + output += `[${logID(peerID)}]${peer.rtcPeer?.connectionState}:${this.getPeername(peerID)}${this.bootstrapPeerIDs?.has(peerID) ? "[Bootstrap]" : ""}` + "\n"; } output += `numActivePeers: ${numActive}` + "\n"; console.log.apply(null, log(output)); @@ -309,9 +322,9 @@ export class PeerManager { // Eventually we want the bootstrap peer to be no different than any other peer anyway. // We should disconnect from the websocket once we connect to our intial peers. // If we have no peer connections, try to connect. If connection fails, start a timer to reconnect. - if (peerID === this.bootstrapPeerID) { - this.bootstrapPeerID = null; - this.bootstrapPeerConnection = null; + if (this.bootstrapPeerIDs?.has(peerID)) { + this.bootstrapPeerIDs.delete(peerID); + this.bootstrapPeerConnections?.delete(peerID); } this.peerStateSuperlog && console.log.apply(null, log(`PeerManager: disconnected from peer ${peerID}`)); this.dispatchEvent(PeerEventTypes.PEER_DISCONNECTED, { peerID: peerID }); @@ -373,10 +386,11 @@ class PeerConnection { this.ignoreOffer = false; this.isSettingRemoteAnswerPending = false; this.polite = true; - this.webRTCSuperlog = true; + this.webRTCSuperlog = false; this.dataChannelSuperlog = false; this.chunkSize = (16 * 1024) - 100; this.messageSuperlog = false; + this.sendQueueSuperLog = false; this.rpcSuperlog = false; this.pendingRPCs = new Map(); this.connectionPromise = null; @@ -406,7 +420,7 @@ class PeerConnection { this.dataChannelSuperlog && console.log.apply(null, log("data channel is open to: ", this.remotePeerID, " from: ", this.peerManager.peerID)); this.send({ type: "hello datachannel", from: this.peerManager.peerID, to: this.remotePeerID }); // this.dataChannel?.send(`{typeHello datachannel from: ${this.peerManager.peerID}`); - console.log.apply(null, log([...this.peerManager.peers.keys()])); + // console.log.apply(null, log([...this.peerManager.peers.keys()])); if (this.peerManager.isBootstrapPeer) { this.send({ type: 'initial_peers', from: this.peerManager.peerID, peers: [...this.peerManager.peers.keys()].filter(entry => entry !== this.remotePeerID) }); // this.dataChannel.send(JSON.stringify()); @@ -565,7 +579,9 @@ class PeerConnection { if (!this.dataChannel) { throw new Error("Send called but datachannel is null"); } + // this.sendQueueSuperLog && console.log.apply(null, log(`[${logID(this.remotePeerID)}]<-datachannel[${logID(this.peerManager.peerID)}]: bufferedAmount ${this.dataChannel.bufferedAmount}`)); while (this.dataChannel.bufferedAmount >= 8 * 1024 * 1024) { + this.sendQueueSuperLog && console.log.apply(null, log(`[${logID(this.remotePeerID)}]<-datachannel[${logID(this.peerManager.peerID)}]: send buffer full, waiting 1 second`)); await new Promise((resolve, reject) => { setTimeout(() => resolve(), 1000); }); @@ -693,9 +709,9 @@ PeerConnection.config = { { urls: "stun:ddln.app" }, // { urls: "turn:ddln.app", username: "a", credential: "b" }, { urls: "stun:stun.l.google.com" }, // keeping this for now as my STUN server is not returning ipv6 - // { urls: "stun:stun1.l.google.com" }, - // { urls: "stun:stun2.l.google.com" }, - // { urls: "stun:stun3.l.google.com" }, - // { urls: "stun:stun4.l.google.com" }, + { urls: "stun:stun1.l.google.com" }, + { urls: "stun:stun2.l.google.com" }, + { urls: "stun:stun3.l.google.com" }, + { urls: "stun:stun4.l.google.com" }, ], };