Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lDEVinux
GitHub Repository: lDEVinux/eaglercraft
Path: blob/main/javascript/eagswebrtc.js
8640 views
1
"use strict";
2
3
/*
4
5
This is the backend for voice channels and LAN servers in eaglercraft
6
7
it links with TeaVM EaglerAdapter at runtime
8
9
Copyright 2022 ayunami2000 & lax1dude. All rights reserved.
10
11
*/
12
13
14
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%% VOICE CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15
16
window.initializeVoiceClient = (() => {
17
18
const READYSTATE_NONE = 0;
19
const READYSTATE_ABORTED = -1;
20
const READYSTATE_DEVICE_INITIALIZED = 1;
21
22
const PEERSTATE_FAILED = 0;
23
const PEERSTATE_SUCCESS = 1;
24
const PEERSTATE_LOADING = 2;
25
26
class EaglercraftVoicePeer {
27
28
constructor(client, peerId, peerConnection, offer) {
29
this.client = client;
30
this.peerId = peerId;
31
this.peerConnection = peerConnection;
32
this.stream = null;
33
34
const self = this;
35
this.peerConnection.addEventListener("icecandidate", (evt) => {
36
if(evt.candidate) {
37
self.client.iceCandidateHandler(self.peerId, JSON.stringify({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate }));
38
}
39
});
40
41
this.peerConnection.addEventListener("track", (evt) => {
42
self.rawStream = evt.streams[0];
43
const aud = new Audio();
44
aud.autoplay = true;
45
aud.muted = true;
46
aud.onended = function() {
47
aud.remove();
48
};
49
aud.srcObject = self.rawStream;
50
self.client.peerTrackHandler(self.peerId, self.rawStream);
51
});
52
53
this.peerConnection.addStream(this.client.localMediaStream.stream);
54
if (offer) {
55
this.peerConnection.createOffer((desc) => {
56
const selfDesc = desc;
57
self.peerConnection.setLocalDescription(selfDesc, () => {
58
self.client.descriptionHandler(self.peerId, JSON.stringify(selfDesc));
59
if (self.client.peerStateInitial != PEERSTATE_SUCCESS) self.client.peerStateInitial = PEERSTATE_SUCCESS;
60
}, (err) => {
61
console.error("Failed to set local description for \"" + self.peerId + "\"! " + err);
62
if (self.client.peerStateInitial == PEERSTATE_LOADING) self.client.peerStateInitial = PEERSTATE_FAILED;
63
self.client.signalDisconnect(self.peerId);
64
});
65
}, (err) => {
66
console.error("Failed to set create offer for \"" + self.peerId + "\"! " + err);
67
if (self.client.peerStateInitial == PEERSTATE_LOADING) self.client.peerStateInitial = PEERSTATE_FAILED;
68
self.client.signalDisconnect(self.peerId);
69
});
70
}
71
72
this.peerConnection.addEventListener("connectionstatechange", (evt) => {
73
if(self.peerConnection.connectionState === 'disconnected') {
74
self.client.signalDisconnect(self.peerId);
75
} else if (self.peerConnection.connectionState === 'connected') {
76
if (self.client.peerState != PEERSTATE_SUCCESS) self.client.peerState = PEERSTATE_SUCCESS;
77
} else if (self.peerConnection.connectionState === 'failed') {
78
if (self.client.peerState == PEERSTATE_LOADING) self.client.peerState = PEERSTATE_FAILED;
79
self.client.signalDisconnect(self.peerId);
80
}
81
});
82
83
}
84
85
disconnect() {
86
this.peerConnection.close();
87
}
88
89
mute(muted) {
90
this.rawStream.getAudioTracks()[0].enabled = !muted;
91
}
92
93
setRemoteDescription(descJSON) {
94
const self = this;
95
try {
96
const remoteDesc = JSON.parse(descJSON);
97
this.peerConnection.setRemoteDescription(remoteDesc, () => {
98
if(remoteDesc.type == 'offer') {
99
self.peerConnection.createAnswer((desc) => {
100
const selfDesc = desc;
101
self.peerConnection.setLocalDescription(selfDesc, () => {
102
self.client.descriptionHandler(self.peerId, JSON.stringify(selfDesc));
103
if (self.client.peerStateDesc != PEERSTATE_SUCCESS) self.client.peerStateDesc = PEERSTATE_SUCCESS;
104
}, (err) => {
105
console.error("Failed to set local description for \"" + self.peerId + "\"! " + err);
106
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
107
self.client.signalDisconnect(self.peerId);
108
});
109
}, (err) => {
110
console.error("Failed to create answer for \"" + self.peerId + "\"! " + err);
111
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
112
self.client.signalDisconnect(self.peerId);
113
});
114
}
115
}, (err) => {
116
console.error("Failed to set remote description for \"" + self.peerId + "\"! " + err);
117
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
118
self.client.signalDisconnect(self.peerId);
119
});
120
} catch (err) {
121
console.error("Failed to parse remote description for \"" + self.peerId + "\"! " + err);
122
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
123
self.client.signalDisconnect(self.peerId);
124
}
125
}
126
127
addICECandidate(candidate) {
128
try {
129
this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(candidate)));
130
if (this.client.peerStateIce != PEERSTATE_SUCCESS) this.client.peerStateIce = PEERSTATE_SUCCESS;
131
} catch (err) {
132
console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err);
133
if (this.client.peerStateIce == PEERSTATE_LOADING) this.client.peerStateIce = PEERSTATE_FAILED;
134
this.client.signalDisconnect(this.peerId);
135
}
136
}
137
138
}
139
140
class EaglercraftVoiceClient {
141
142
constructor() {
143
this.ICEServers = [];
144
this.hasInit = false;
145
this.peerList = new Map();
146
this.readyState = READYSTATE_NONE;
147
this.peerState = PEERSTATE_LOADING;
148
this.peerStateConnect = PEERSTATE_LOADING;
149
this.peerStateInitial = PEERSTATE_LOADING;
150
this.peerStateDesc = PEERSTATE_LOADING;
151
this.peerStateIce = PEERSTATE_LOADING;
152
this.iceCandidateHandler = null;
153
this.descriptionHandler = null;
154
this.peerTrackHandler = null;
155
this.peerDisconnectHandler = null;
156
this.microphoneVolumeAudioContext = null;
157
}
158
159
voiceClientSupported() {
160
return typeof window.RTCPeerConnection !== "undefined" && typeof navigator.mediaDevices !== "undefined" &&
161
typeof navigator.mediaDevices.getUserMedia !== "undefined";
162
}
163
164
setICEServers(urls) {
165
this.ICEServers.length = 0;
166
for(var i = 0; i < urls.length; ++i) {
167
var etr = urls[i].split(";");
168
if(etr.length == 1) {
169
this.ICEServers.push({ urls: etr[0] });
170
}else if(etr.length == 3) {
171
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
172
}
173
}
174
}
175
176
setICECandidateHandler(cb) {
177
this.iceCandidateHandler = cb;
178
}
179
180
setDescriptionHandler(cb) {
181
this.descriptionHandler = cb;
182
}
183
184
setPeerTrackHandler(cb) {
185
this.peerTrackHandler = cb;
186
}
187
188
setPeerDisconnectHandler(cb) {
189
this.peerDisconnectHandler = cb;
190
}
191
192
activateVoice(tk) {
193
if(this.hasInit) this.localRawMediaStream.getAudioTracks()[0].enabled = tk;
194
}
195
196
initializeDevices() {
197
if(!this.hasInit) {
198
const self = this;
199
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
200
self.microphoneVolumeAudioContext = new AudioContext();
201
self.localRawMediaStream = stream;
202
self.localRawMediaStream.getAudioTracks()[0].enabled = false;
203
self.localMediaStream = self.microphoneVolumeAudioContext.createMediaStreamDestination();
204
self.localMediaStreamGain = self.microphoneVolumeAudioContext.createGain();
205
var localStreamIn = self.microphoneVolumeAudioContext.createMediaStreamSource(stream);
206
localStreamIn.connect(self.localMediaStreamGain);
207
self.localMediaStreamGain.connect(self.localMediaStream);
208
self.localMediaStreamGain.gain.value = 1.0;
209
self.readyState = READYSTATE_DEVICE_INITIALIZED;
210
this.hasInit = true;
211
}).catch((err) => {
212
self.readyState = READYSTATE_ABORTED;
213
});
214
}else {
215
this.readyState = READYSTATE_DEVICE_INITIALIZED;
216
}
217
}
218
219
setMicVolume(val) {
220
if(this.hasInit) {
221
if(val > 0.5) val = 0.5 + (val - 0.5) * 2.0;
222
if(val > 1.5) val = 1.5;
223
if(val < 0.0) val = 0.0;
224
this.localMediaStreamGain.gain.value = val * 2.0;
225
}
226
}
227
228
resetPeerStates() {
229
this.peerState = this.peerStateConnect = this.peerStateInitial = this.peerStateDesc = this.peerStateIce = PEERSTATE_LOADING;
230
}
231
232
getPeerState() {
233
return this.peerState;
234
}
235
236
getPeerStateConnect() {
237
return this.peerStateConnect;
238
}
239
240
getPeerStateInitial() {
241
return this.peerStateInitial;
242
}
243
244
getPeerStateDesc() {
245
return this.peerStateDesc;
246
}
247
248
getPeerStateIce() {
249
return this.peerStateIce;
250
}
251
252
getReadyState() {
253
return this.readyState;
254
}
255
256
signalConnect(peerId, offer) {
257
if (!this.hasInit) this.initializeDevices();
258
try {
259
const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
260
const peerInstance = new EaglercraftVoicePeer(this, peerId, peerConnection, offer);
261
this.peerList.set(peerId, peerInstance);
262
if (this.peerStateConnect != PEERSTATE_SUCCESS) this.peerStateConnect = PEERSTATE_SUCCESS;
263
} catch (e) {
264
if (this.peerStateConnect == PEERSTATE_LOADING) this.peerStateConnect = PEERSTATE_FAILED;
265
}
266
}
267
268
signalDescription(peerId, descJSON) {
269
var thePeer = this.peerList.get(peerId);
270
if((typeof thePeer !== "undefined") && thePeer !== null) {
271
thePeer.setRemoteDescription(descJSON);
272
}
273
}
274
275
signalDisconnect(peerId, quiet) {
276
var thePeer = this.peerList.get(peerId);
277
if((typeof thePeer !== "undefined") && thePeer !== null) {
278
this.peerList.delete(thePeer);
279
try {
280
thePeer.disconnect();
281
}catch(e) {}
282
this.peerDisconnectHandler(peerId, quiet);
283
}
284
}
285
286
mutePeer(peerId, muted) {
287
var thePeer = this.peerList.get(peerId);
288
if((typeof thePeer !== "undefined") && thePeer !== null) {
289
thePeer.mute(muted);
290
}
291
}
292
293
signalICECandidate(peerId, candidate) {
294
var thePeer = this.peerList.get(peerId);
295
if((typeof thePeer !== "undefined") && thePeer !== null) {
296
thePeer.addICECandidate(candidate);
297
}
298
}
299
300
}
301
302
window.constructVoiceClient = () => new EaglercraftVoiceClient();
303
});
304
305
window.startVoiceClient = () => {
306
if(typeof window.constructVoiceClient !== "function") {
307
window.initializeVoiceClient();
308
}
309
return window.constructVoiceClient();
310
};
311
312
313
314
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%% LAN CLIENT CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315
316
window.initializeLANClient = (() => {
317
318
const READYSTATE_INIT_FAILED = -2;
319
const READYSTATE_FAILED = -1;
320
const READYSTATE_DISCONNECTED = 0;
321
const READYSTATE_CONNECTING = 1;
322
const READYSTATE_CONNECTED = 2;
323
324
class EaglercraftLANClient {
325
326
constructor() {
327
this.ICEServers = [];
328
this.peerConnection = null;
329
this.dataChannel = null;
330
this.readyState = READYSTATE_CONNECTING;
331
this.iceCandidateHandler = null;
332
this.descriptionHandler = null;
333
this.remoteDataChannelHandler = null;
334
this.remoteDisconnectHandler = null;
335
this.remotePacketHandler = null;
336
}
337
338
LANClientSupported() {
339
return typeof window.RTCPeerConnection !== "undefined";
340
}
341
342
initializeClient() {
343
try {
344
if(this.dataChannel != null) {
345
this.dataChannel.close();
346
this.dataChannel = null;
347
}
348
if(this.peerConnection != null) {
349
this.peerConnection.close();
350
}
351
this.peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
352
this.readyState = READYSTATE_CONNECTING;
353
} catch (e) {
354
this.readyState = READYSTATE_INIT_FAILED;
355
}
356
}
357
358
setICEServers(urls) {
359
this.ICEServers.length = 0;
360
for(var i = 0; i < urls.length; ++i) {
361
var etr = urls[i].split(";");
362
if(etr.length == 1) {
363
this.ICEServers.push({ urls: etr[0] });
364
}else if(etr.length == 3) {
365
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
366
}
367
}
368
}
369
370
setICECandidateHandler(cb) {
371
this.iceCandidateHandler = cb;
372
}
373
374
setDescriptionHandler(cb) {
375
this.descriptionHandler = cb;
376
}
377
378
setRemoteDataChannelHandler(cb) {
379
this.remoteDataChannelHandler = cb;
380
}
381
382
setRemoteDisconnectHandler(cb) {
383
this.remoteDisconnectHandler = cb;
384
}
385
386
setRemotePacketHandler(cb) {
387
this.remotePacketHandler = cb;
388
}
389
390
getReadyState() {
391
return this.readyState;
392
}
393
394
sendPacketToServer(buffer) {
395
if(this.dataChannel != null && this.dataChannel.readyState == "open") {
396
this.dataChannel.send(buffer);
397
}else {
398
this.signalRemoteDisconnect(false);
399
}
400
}
401
402
signalRemoteConnect() {
403
const self = this;
404
405
const iceCandidates = [];
406
407
this.peerConnection.addEventListener("icecandidate", (evt) => {
408
if(evt.candidate) {
409
if(iceCandidates.length == 0) setTimeout(() => {
410
if(self.peerConnection != null && self.peerConnection.connectionState != "disconnected") {
411
self.iceCandidateHandler(JSON.stringify(iceCandidates));
412
iceCandidates.length = 0;
413
}
414
}, 3000);
415
iceCandidates.push({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate });
416
}
417
});
418
419
this.dataChannel = this.peerConnection.createDataChannel("lan");
420
this.dataChannel.binaryType = "arraybuffer";
421
422
this.dataChannel.addEventListener("open", async (evt) => {
423
while(iceCandidates.length > 0) {
424
await new Promise(resolve => setTimeout(resolve, 0));
425
}
426
self.remoteDataChannelHandler(self.dataChannel);
427
});
428
429
this.dataChannel.addEventListener("message", (evt) => {
430
self.remotePacketHandler(evt.data);
431
}, false);
432
433
this.peerConnection.createOffer((desc) => {
434
const selfDesc = desc;
435
self.peerConnection.setLocalDescription(selfDesc, () => {
436
self.descriptionHandler(JSON.stringify(selfDesc));
437
}, (err) => {
438
console.error("Failed to set local description! " + err);
439
self.readyState = READYSTATE_FAILED;
440
self.signalRemoteDisconnect(false);
441
});
442
}, (err) => {
443
console.error("Failed to set create offer! " + err);
444
self.readyState = READYSTATE_FAILED;
445
self.signalRemoteDisconnect(false);
446
});
447
448
this.peerConnection.addEventListener("connectionstatechange", (evt) => {
449
if(self.peerConnection.connectionState === 'disconnected') {
450
self.signalRemoteDisconnect(false);
451
} else if (self.peerConnection.connectionState === 'connected') {
452
self.readyState = READYSTATE_CONNECTED;
453
} else if (self.peerConnection.connectionState === 'failed') {
454
self.readyState = READYSTATE_FAILED;
455
self.signalRemoteDisconnect(false);
456
}
457
});
458
}
459
460
signalRemoteDescription(descJSON) {
461
try {
462
this.peerConnection.setRemoteDescription(JSON.parse(descJSON));
463
} catch (e) {
464
console.error(e);
465
this.readyState = READYSTATE_FAILED;
466
this.signalRemoteDisconnect(false);
467
}
468
}
469
470
signalRemoteICECandidate(candidates) {
471
try {
472
const candidateList = JSON.parse(candidates);
473
for (let candidate of candidateList) {
474
this.peerConnection.addIceCandidate(candidate);
475
}
476
} catch (e) {
477
console.error(e);
478
this.readyState = READYSTATE_FAILED;
479
this.signalRemoteDisconnect(false);
480
}
481
}
482
483
signalRemoteDisconnect(quiet) {
484
if(this.dataChannel != null) {
485
this.dataChannel.close();
486
this.dataChannel = null;
487
}
488
if(this.peerConnection != null) {
489
this.peerConnection.close();
490
}
491
if(!quiet) this.remoteDisconnectHandler();
492
this.readyState = READYSTATE_DISCONNECTED;
493
}
494
495
};
496
497
window.constructLANClient = () => new EaglercraftLANClient();
498
});
499
500
window.startLANClient = () => {
501
if(typeof window.constructLANClient !== "function") {
502
window.initializeLANClient();
503
}
504
return window.constructLANClient();
505
};
506
507
508
509
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%% LAN SERVER CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510
511
window.initializeLANServer = (() => {
512
513
const PEERSTATE_FAILED = 0;
514
const PEERSTATE_SUCCESS = 1;
515
const PEERSTATE_LOADING = 2;
516
517
class EaglercraftLANPeer {
518
519
constructor(client, peerId, peerConnection) {
520
this.client = client;
521
this.peerId = peerId;
522
this.peerConnection = peerConnection;
523
this.dataChannel = null;
524
525
const self = this;
526
527
const iceCandidates = [];
528
529
this.peerConnection.addEventListener("icecandidate", (evt) => {
530
if(evt.candidate) {
531
if(iceCandidates.length == 0) setTimeout(() => {
532
if(self.peerConnection != null && self.peerConnection.connectionState != "disconnected") {
533
self.client.iceCandidateHandler(self.peerId, JSON.stringify(iceCandidates));
534
iceCandidates.length = 0;
535
}
536
}, 3000);
537
iceCandidates.push({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate });
538
}
539
});
540
541
this.peerConnection.addEventListener("datachannel", async (evt) => {
542
while(iceCandidates.length > 0) {
543
await new Promise(resolve => setTimeout(resolve, 0));
544
}
545
self.dataChannel = evt.channel;
546
self.client.remoteClientDataChannelHandler(self.peerId, self.dataChannel);
547
self.dataChannel.addEventListener("message", (evt) => {
548
self.client.remoteClientPacketHandler(self.peerId, evt.data);
549
}, false);
550
}, false);
551
552
this.peerConnection.addEventListener("connectionstatechange", (evt) => {
553
if(self.peerConnection.connectionState === 'disconnected') {
554
self.client.signalRemoteDisconnect(self.peerId);
555
} else if (self.peerConnection.connectionState === 'connected') {
556
if (self.client.peerState != PEERSTATE_SUCCESS) self.client.peerState = PEERSTATE_SUCCESS;
557
} else if (self.peerConnection.connectionState === 'failed') {
558
if (self.client.peerState == PEERSTATE_LOADING) self.client.peerState = PEERSTATE_FAILED;
559
self.client.signalRemoteDisconnect(self.peerId);
560
}
561
});
562
563
}
564
565
disconnect() {
566
if(this.dataChannel != null) {
567
this.dataChannel.close();
568
this.dataChannel = null;
569
}
570
this.peerConnection.close();
571
}
572
573
setRemoteDescription(descJSON) {
574
const self = this;
575
try {
576
const remoteDesc = JSON.parse(descJSON);
577
this.peerConnection.setRemoteDescription(remoteDesc, () => {
578
if(remoteDesc.type == 'offer') {
579
self.peerConnection.createAnswer((desc) => {
580
const selfDesc = desc;
581
self.peerConnection.setLocalDescription(selfDesc, () => {
582
self.client.descriptionHandler(self.peerId, JSON.stringify(selfDesc));
583
if (self.client.peerStateDesc != PEERSTATE_SUCCESS) self.client.peerStateDesc = PEERSTATE_SUCCESS;
584
}, (err) => {
585
console.error("Failed to set local description for \"" + self.peerId + "\"! " + err);
586
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
587
self.client.signalRemoteDisconnect(self.peerId);
588
});
589
}, (err) => {
590
console.error("Failed to create answer for \"" + self.peerId + "\"! " + err);
591
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
592
self.client.signalRemoteDisconnect(self.peerId);
593
});
594
}
595
}, (err) => {
596
console.error("Failed to set remote description for \"" + self.peerId + "\"! " + err);
597
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
598
self.client.signalRemoteDisconnect(self.peerId);
599
});
600
} catch (err) {
601
console.error("Failed to parse remote description for \"" + self.peerId + "\"! " + err);
602
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED;
603
self.client.signalRemoteDisconnect(self.peerId);
604
}
605
}
606
607
addICECandidate(candidates) {
608
try {
609
const candidateList = JSON.parse(candidates);
610
for (let candidate of candidateList) {
611
this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
612
}
613
if (this.client.peerStateIce != PEERSTATE_SUCCESS) this.client.peerStateIce = PEERSTATE_SUCCESS;
614
} catch (err) {
615
console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err);
616
if (this.client.peerStateIce == PEERSTATE_LOADING) this.client.peerStateIce = PEERSTATE_FAILED;
617
this.client.signalRemoteDisconnect(this.peerId);
618
}
619
}
620
621
}
622
623
class EaglercraftLANServer {
624
625
constructor() {
626
this.ICEServers = [];
627
this.hasInit = false;
628
this.peerList = new Map();
629
this.peerState = PEERSTATE_LOADING;
630
this.peerStateConnect = PEERSTATE_LOADING;
631
this.peerStateInitial = PEERSTATE_LOADING;
632
this.peerStateDesc = PEERSTATE_LOADING;
633
this.peerStateIce = PEERSTATE_LOADING;
634
this.iceCandidateHandler = null;
635
this.descriptionHandler = null;
636
this.remoteClientDataChannelHandler = null;
637
this.remoteClientDisconnectHandler = null;
638
this.remoteClientPacketHandler = null;
639
}
640
641
LANServerSupported() {
642
return typeof window.RTCPeerConnection !== "undefined";
643
}
644
645
initializeServer() {
646
// nothing to do!
647
}
648
649
setICEServers(urls) {
650
this.ICEServers.length = 0;
651
for(var i = 0; i < urls.length; ++i) {
652
var etr = urls[i].split(";");
653
if(etr.length == 1) {
654
this.ICEServers.push({ urls: etr[0] });
655
}else if(etr.length == 3) {
656
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
657
}
658
}
659
}
660
661
setICECandidateHandler(cb) {
662
this.iceCandidateHandler = cb;
663
}
664
665
setDescriptionHandler(cb) {
666
this.descriptionHandler = cb;
667
}
668
669
setRemoteClientDataChannelHandler(cb) {
670
this.remoteClientDataChannelHandler = cb;
671
}
672
673
setRemoteClientDisconnectHandler(cb) {
674
this.remoteClientDisconnectHandler = cb;
675
}
676
677
setRemoteClientPacketHandler(cb) {
678
this.remoteClientPacketHandler = cb;
679
}
680
681
sendPacketToRemoteClient(peerId, buffer) {
682
var thePeer = this.peerList.get(peerId);
683
if((typeof thePeer !== "undefined") && thePeer !== null) {
684
if(thePeer.dataChannel != null && thePeer.dataChannel.readyState == "open") {
685
thePeer.dataChannel.send(buffer);
686
}else {
687
this.signalRemoteDisconnect(peerId);
688
}
689
}
690
}
691
692
resetPeerStates() {
693
this.peerState = this.peerStateConnect = this.peerStateInitial = this.peerStateDesc = this.peerStateIce = PEERSTATE_LOADING;
694
}
695
696
getPeerState() {
697
return this.peerState;
698
}
699
700
getPeerStateConnect() {
701
return this.peerStateConnect;
702
}
703
704
getPeerStateInitial() {
705
return this.peerStateInitial;
706
}
707
708
getPeerStateDesc() {
709
return this.peerStateDesc;
710
}
711
712
getPeerStateIce() {
713
return this.peerStateIce;
714
}
715
716
signalRemoteConnect(peerId) {
717
try {
718
const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
719
const peerInstance = new EaglercraftLANPeer(this, peerId, peerConnection);
720
this.peerList.set(peerId, peerInstance);
721
if (this.peerStateConnect != PEERSTATE_SUCCESS) this.peerStateConnect = PEERSTATE_SUCCESS;
722
} catch (e) {
723
if (this.peerStateConnect == PEERSTATE_LOADING) this.peerStateConnect = PEERSTATE_FAILED;
724
}
725
}
726
727
signalRemoteDescription(peerId, descJSON) {
728
var thePeer = this.peerList.get(peerId);
729
if((typeof thePeer !== "undefined") && thePeer !== null) {
730
thePeer.setRemoteDescription(descJSON);
731
}
732
}
733
734
signalRemoteICECandidate(peerId, candidate) {
735
var thePeer = this.peerList.get(peerId);
736
if((typeof thePeer !== "undefined") && thePeer !== null) {
737
thePeer.addICECandidate(candidate);
738
}
739
}
740
741
signalRemoteDisconnect(peerId) {
742
if(peerId.length == 0) {
743
for(const thePeer of this.peerList.values()) {
744
if((typeof thePeer !== "undefined") && thePeer !== null) {
745
this.peerList.delete(peerId);
746
try {
747
thePeer.disconnect();
748
}catch(e) {}
749
this.remoteClientDisconnectHandler(peerId);
750
}
751
}
752
this.peerList.clear();
753
return;
754
}
755
var thePeer = this.peerList.get(peerId);
756
if((typeof thePeer !== "undefined") && thePeer !== null) {
757
this.peerList.delete(peerId);
758
try {
759
thePeer.disconnect();
760
}catch(e) {}
761
this.remoteClientDisconnectHandler(peerId);
762
}
763
}
764
765
countPeers() {
766
return this.peerList.size;
767
}
768
769
};
770
771
window.constructLANServer = () => new EaglercraftLANServer();
772
});
773
774
window.startLANServer = () => {
775
if(typeof window.constructLANServer !== "function") {
776
window.initializeLANServer();
777
}
778
return window.constructLANServer();
779
};
780
781