-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSwarm.js
156 lines (146 loc) · 5.67 KB
/
Swarm.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
//import 'simple-peer';
import { EventBase } from './EventBase.js';
import { query_match_data } from './TaiiNet.js';
// Represents a connection to a group of peers
export class Swarm extends EventBase {
constructor(signaller) {
super();
this.signaller = signaller;
this.all_peers = {};
this.connected_peers = {};
// handle signal messages from the signal server
this.signaller.on("signal", function (signal) {
console.log(signal);
if (this.all_peers[signal.from_id] == undefined) {
// this signal is from a new peer
var peer = this.create_peer(
signal.from_id, signal.data.query, this.signaller.signal.bind(this.signaller), false);
// add it to the list of peers to mark that we're already connecting
this.all_peers[signal.from_id] = peer;
}
else {
// we already know this peer
var peer = this.all_peers[signal.from_id];
}
// give this peer our signal
peer.signal(signal.data.signal_data);
}.bind(this));
}
// creates a new simple peer object, and adds the hooks it needs to auto connect
create_peer(sid, query, signal, initiator) {
// initiate peer object
var initiator = initiator || undefined;
var peer = new SimplePeer({ initiator: initiator });
peer.message_queue = [];
// handle peer signals by proxying them through the signaller
peer.on("signal", function (data) {
signal.bind(this.signaller)(sid, {
signal_data: data,
query: query
}, "signal");
}.bind(this));
peer.on("connect", function (data) {
peer._channel.query = query;
this.handle_datachannel(sid, peer._channel);
}.bind(this));
// a hack to fix a simplepeer bug
// basically, sometimes the "connect" event isn't called even though the
// datachannel is connected. To combat this, we just set a new
// ondatachannel callback, and manually call the original callback
var simplepeer_callback = peer._pc.ondatachannel;
peer._pc.ondatachannel = function (event) {
//simplepeer_callback(event);
// a timeout is needed here to drop the send method to the end of the
// call stack. Note that the timeout duration is 0ms and should not cause
// timing issues
setTimeout(function () {
console.log("firing peer-connected from signaller");
event.channel.query = query;
this.handle_datachannel(sid, event.channel);
}.bind(this), 0);
}.bind(this);
var disconnect = function () {
// purge the disconnected peer from the swarm
for (var i in this.all_peers) {
if (this.all_peers[i].status == "disconnected") {
var dead_peer = this.all_peers[i];
this.all_peers = this.all_peers.splice(
this.all_peers.indexOf(dead_peer), 1
);
this.connected_peers = this.connected_peers.splice(
this.connected_peers.indexOf(dead_peer), 1
);
break;
}
}
// we lost a peer connection
this.trigger("peer-disconnected", dead_peer);
}.bind(this);
peer.on("close", disconnect);
peer.on("error", disconnect);
return peer;
}
// connect to a given socket
connect(sid, query, via) {
if (this.all_peers[sid] == undefined) {
// create hooks
if (via == undefined) {
var peer = this.create_peer(sid, query, this.signaller.signal, true);
}
else {
// not implemented
//var peer = this.create_peer(sid, query, this.connected_peers[via].send_direct, true);
}
// file our new connection for later use
this.all_peers[sid] = peer;
}
else {
var peer = this.all_peers[sid];
}
return peer;
}
// event handler for when a datachannel has finished connecting
handle_datachannel(sid, peer) {
// add the peer to the list of connected peers
console.log(peer);
peer.onmessage = this.handle_data.bind(this);
this.connected_peers[sid] = peer;
this.trigger("peer-connected", peer);
// if any messages were sent when there were no connected peers, send
// them all now
for (var q in peer.message_queue) {
var message = peer.message_queue[q];
peer.send(message);
}
}
// when we get new data from a node on the network
handle_data(e) {
// is it data or a signal
var data = JSON.parse(e.data)
if (data.type == "data") {
this.trigger("data", data, e);
}
else if (data.type == "signal") {
// TODO: implement via peer signalling
}
}
// send data directly to a connected peer,
send_direct(peer, data, type) {
var payload = JSON.stringify({
type: type,
data: data
});
peer.send(payload);
}
// sends data to all relevant connected peers
send(data, type, peers) {
var type = type || "data";
var peers = peers || this.connected_peers;
for (var p in peers) {
var peer = peers[p];
if (query_match_data(peer.query, data)) {
this.send_direct(peer, data, type);
}
}
}
}