eaglercraft-1.5/javascript/eagswebrtc.js

697 lines
20 KiB
JavaScript
Raw Normal View History

2022-07-16 22:02:55 -07:00
"use strict";
/*
2022-08-06 15:33:14 -07:00
This is the backend for voice channels and LAN servers in eaglercraft
2022-07-16 22:02:55 -07:00
2022-08-06 15:33:14 -07:00
it links with TeaVM EaglerAdapter at runtime
2022-07-16 22:02:55 -07:00
2022-08-06 15:33:14 -07:00
Copyright 2022 ayunami2000 & lax1dude. All rights reserved.
2022-07-16 22:02:55 -07:00
*/
2022-08-06 15:33:14 -07:00
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%% VOICE CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2022-07-16 22:02:55 -07:00
window.initializeVoiceClient = (() => {
const READYSTATE_NONE = 0;
const READYSTATE_ABORTED = -1;
const READYSTATE_DEVICE_INITIALIZED = 1;
class EaglercraftVoicePeer {
2022-07-17 17:57:05 -07:00
constructor(client, peerId, peerConnection, offer) {
2022-07-16 22:02:55 -07:00
this.client = client;
this.peerId = peerId;
this.peerConnection = peerConnection;
2022-07-17 17:57:05 -07:00
this.stream = null;
2022-07-16 22:02:55 -07:00
this.peerConnection.addEventListener("icecandidate", (evt) => {
if(evt.candidate) {
2024-12-08 23:43:32 -08:00
this.client.iceCandidateHandler(this.peerId, JSON.stringify({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate }));
2022-07-16 22:02:55 -07:00
}
});
this.peerConnection.addEventListener("track", (evt) => {
2024-12-08 23:43:32 -08:00
this.rawStream = evt.streams[0];
2022-07-17 17:57:05 -07:00
const aud = new Audio();
aud.autoplay = true;
aud.muted = true;
aud.onended = function() {
aud.remove();
};
2024-12-08 23:43:32 -08:00
aud.srcObject = this.rawStream;
this.client.peerTrackHandler(this.peerId, this.rawStream);
2022-07-16 22:02:55 -07:00
});
2022-07-17 17:57:05 -07:00
this.peerConnection.addStream(this.client.localMediaStream.stream);
if (offer) {
this.peerConnection.createOffer((desc) => {
const selfDesc = desc;
2024-12-08 23:43:32 -08:00
this.peerConnection.setLocalDescription(selfDesc, () => {
this.client.descriptionHandler(this.peerId, JSON.stringify(selfDesc));
2022-07-17 17:57:05 -07:00
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to set local description for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-17 17:57:05 -07:00
});
2022-07-16 22:02:55 -07:00
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to set create offer for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-16 22:02:55 -07:00
});
2022-07-17 17:57:05 -07:00
}
2022-08-21 19:47:23 -07:00
2022-07-16 22:02:55 -07:00
this.peerConnection.addEventListener("connectionstatechange", (evt) => {
2024-12-08 23:43:32 -08:00
if(this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed') {
this.client.signalDisconnect(this.peerId);
2022-07-16 22:02:55 -07:00
}
});
}
disconnect() {
this.peerConnection.close();
}
2022-07-17 17:57:05 -07:00
mute(muted) {
this.rawStream.getAudioTracks()[0].enabled = !muted;
}
2022-07-16 22:02:55 -07:00
setRemoteDescription(descJSON) {
2022-07-17 17:57:05 -07:00
try {
const remoteDesc = JSON.parse(descJSON);
this.peerConnection.setRemoteDescription(remoteDesc, () => {
2024-12-08 23:43:32 -08:00
if(remoteDesc.type === 'offer') {
this.peerConnection.createAnswer((desc) => {
2022-07-17 17:57:05 -07:00
const selfDesc = desc;
2024-12-08 23:43:32 -08:00
this.peerConnection.setLocalDescription(selfDesc, () => {
this.client.descriptionHandler(this.peerId, JSON.stringify(selfDesc));
2022-07-17 17:57:05 -07:00
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to set local description for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-17 17:57:05 -07:00
});
2022-07-16 22:02:55 -07:00
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to create answer for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-16 22:02:55 -07:00
});
2022-07-17 17:57:05 -07:00
}
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to set remote description for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-17 17:57:05 -07:00
});
} catch (err) {
2024-12-08 23:43:32 -08:00
console.error("Failed to parse remote description for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-17 17:57:05 -07:00
}
2022-07-16 22:02:55 -07:00
}
addICECandidate(candidate) {
2022-07-17 17:57:05 -07:00
try {
this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(candidate)));
} catch (err) {
2022-07-18 23:00:13 -07:00
console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err);
this.client.signalDisconnect(this.peerId);
2022-07-17 17:57:05 -07:00
}
2022-07-16 22:02:55 -07:00
}
}
class EaglercraftVoiceClient {
constructor() {
this.ICEServers = [];
this.hasInit = false;
this.peerList = new Map();
this.readyState = READYSTATE_NONE;
this.iceCandidateHandler = null;
this.descriptionHandler = null;
this.peerTrackHandler = null;
this.peerDisconnectHandler = null;
2022-07-18 08:07:59 -07:00
this.microphoneVolumeAudioContext = null;
2022-07-16 22:02:55 -07:00
}
voiceClientSupported() {
return typeof window.RTCPeerConnection !== "undefined" && typeof navigator.mediaDevices !== "undefined" &&
typeof navigator.mediaDevices.getUserMedia !== "undefined";
}
setICEServers(urls) {
this.ICEServers.length = 0;
2022-07-17 23:49:05 -07:00
for(var i = 0; i < urls.length; ++i) {
var etr = urls[i].split(";");
2024-12-08 23:43:32 -08:00
if(etr.length === 1) {
2022-07-17 23:49:05 -07:00
this.ICEServers.push({ urls: etr[0] });
2024-12-08 23:43:32 -08:00
}else if(etr.length === 3) {
2022-07-17 23:49:05 -07:00
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
2022-07-17 17:57:05 -07:00
}
2022-07-16 22:02:55 -07:00
}
}
setICECandidateHandler(cb) {
this.iceCandidateHandler = cb;
}
setDescriptionHandler(cb) {
this.descriptionHandler = cb;
}
setPeerTrackHandler(cb) {
this.peerTrackHandler = cb;
}
setPeerDisconnectHandler(cb) {
this.peerDisconnectHandler = cb;
}
activateVoice(tk) {
2022-07-17 17:57:05 -07:00
if(this.hasInit) this.localRawMediaStream.getAudioTracks()[0].enabled = tk;
2022-07-16 22:02:55 -07:00
}
2022-07-17 17:57:05 -07:00
initializeDevices() {
2022-07-16 22:02:55 -07:00
if(!this.hasInit) {
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
2024-12-08 23:43:32 -08:00
this.microphoneVolumeAudioContext = new AudioContext();
this.localRawMediaStream = stream;
this.localRawMediaStream.getAudioTracks()[0].enabled = false;
this.localMediaStream = this.microphoneVolumeAudioContext.createMediaStreamDestination();
this.localMediaStreamGain = this.microphoneVolumeAudioContext.createGain();
var localStreamIn = this.microphoneVolumeAudioContext.createMediaStreamSource(stream);
localStreamIn.connect(this.localMediaStreamGain);
this.localMediaStreamGain.connect(this.localMediaStream);
this.localMediaStreamGain.gain.value = 1.0;
this.readyState = READYSTATE_DEVICE_INITIALIZED;
2022-07-16 22:02:55 -07:00
this.hasInit = true;
2022-07-17 17:57:05 -07:00
}).catch((err) => {
2024-12-08 23:43:32 -08:00
this.readyState = READYSTATE_ABORTED;
2022-07-16 22:02:55 -07:00
});
}else {
2022-07-18 23:00:13 -07:00
this.readyState = READYSTATE_DEVICE_INITIALIZED;
2022-07-16 22:02:55 -07:00
}
}
setMicVolume(val) {
2022-07-17 17:57:05 -07:00
if(this.hasInit) {
if(val > 0.5) val = 0.5 + (val - 0.5) * 2.0;
if(val > 1.5) val = 1.5;
if(val < 0.0) val = 0.0;
this.localMediaStreamGain.gain.value = val * 2.0;
}
2022-07-16 22:02:55 -07:00
}
getReadyState() {
return this.readyState;
}
2022-07-17 17:57:05 -07:00
signalConnect(peerId, offer) {
2022-07-18 23:00:13 -07:00
try {
2024-12-08 23:43:32 -08:00
const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
const peerInstance = new EaglercraftVoicePeer(this, peerId, peerConnection, offer);
this.peerList.set(peerId, peerInstance);
2022-07-18 23:00:13 -07:00
} catch (e) {
}
2022-07-16 22:02:55 -07:00
}
signalDescription(peerId, descJSON) {
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
thePeer.setRemoteDescription(descJSON);
}
}
2022-07-17 17:57:05 -07:00
signalDisconnect(peerId, quiet) {
2022-07-16 22:02:55 -07:00
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
this.peerList.delete(thePeer);
try {
thePeer.disconnect();
}catch(e) {}
2022-07-17 17:57:05 -07:00
this.peerDisconnectHandler(peerId, quiet);
}
}
mutePeer(peerId, muted) {
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
thePeer.mute(muted);
2022-07-16 22:02:55 -07:00
}
}
signalICECandidate(peerId, candidate) {
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
thePeer.addICECandidate(candidate);
}
}
}
window.constructVoiceClient = () => new EaglercraftVoiceClient();
});
window.startVoiceClient = () => {
if(typeof window.constructVoiceClient !== "function") {
window.initializeVoiceClient();
}
return window.constructVoiceClient();
2022-08-06 15:33:14 -07:00
};
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%% LAN CLIENT CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
window.initializeLANClient = (() => {
2022-08-10 08:52:43 -07:00
2022-08-06 15:33:14 -07:00
const READYSTATE_INIT_FAILED = -2;
const READYSTATE_FAILED = -1;
const READYSTATE_DISCONNECTED = 0;
const READYSTATE_CONNECTING = 1;
const READYSTATE_CONNECTED = 2;
class EaglercraftLANClient {
2022-08-10 08:52:43 -07:00
2022-08-06 15:33:14 -07:00
constructor() {
2022-08-10 08:52:43 -07:00
this.ICEServers = [];
this.peerConnection = null;
this.dataChannel = null;
this.readyState = READYSTATE_CONNECTING;
this.iceCandidateHandler = null;
this.descriptionHandler = null;
this.remoteDataChannelHandler = null;
this.remoteDisconnectHandler = null;
this.remotePacketHandler = null;
2022-08-06 15:33:14 -07:00
}
LANClientSupported() {
2022-08-10 08:52:43 -07:00
return typeof window.RTCPeerConnection !== "undefined";
2022-08-06 15:33:14 -07:00
}
initializeClient() {
2022-08-10 08:52:43 -07:00
try {
2024-12-08 23:43:32 -08:00
if(this.dataChannel !== null) {
2022-08-10 08:52:43 -07:00
this.dataChannel.close();
this.dataChannel = null;
}
2024-12-08 23:43:32 -08:00
if(this.peerConnection !== null) {
2022-08-10 08:52:43 -07:00
this.peerConnection.close();
}
this.peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
this.readyState = READYSTATE_CONNECTING;
} catch (e) {
this.readyState = READYSTATE_INIT_FAILED;
}
2022-08-06 15:33:14 -07:00
}
setICEServers(urls) {
2022-08-10 08:52:43 -07:00
this.ICEServers.length = 0;
for(var i = 0; i < urls.length; ++i) {
var etr = urls[i].split(";");
2024-12-08 23:43:32 -08:00
if(etr.length === 1) {
2022-08-10 08:52:43 -07:00
this.ICEServers.push({ urls: etr[0] });
2024-12-08 23:43:32 -08:00
}else if(etr.length === 3) {
2022-08-10 08:52:43 -07:00
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
}
}
2022-08-06 15:33:14 -07:00
}
setICECandidateHandler(cb) {
2022-08-10 08:52:43 -07:00
this.iceCandidateHandler = cb;
2022-08-06 15:33:14 -07:00
}
setDescriptionHandler(cb) {
2022-08-10 08:52:43 -07:00
this.descriptionHandler = cb;
2022-08-06 15:33:14 -07:00
}
setRemoteDataChannelHandler(cb) {
2022-08-10 08:52:43 -07:00
this.remoteDataChannelHandler = cb;
2022-08-06 15:33:14 -07:00
}
setRemoteDisconnectHandler(cb) {
2022-08-10 08:52:43 -07:00
this.remoteDisconnectHandler = cb;
2022-08-06 15:33:14 -07:00
}
setRemotePacketHandler(cb) {
2022-08-10 08:52:43 -07:00
this.remotePacketHandler = cb;
2022-08-06 15:33:14 -07:00
}
getReadyState() {
2022-08-10 08:52:43 -07:00
return this.readyState;
2022-08-06 15:33:14 -07:00
}
sendPacketToServer(buffer) {
2024-12-08 23:43:32 -08:00
if(this.dataChannel !== null && this.dataChannel.readyState === "open") {
2022-08-19 20:21:08 -07:00
this.dataChannel.send(buffer);
}else {
this.signalRemoteDisconnect(false);
}
2022-08-06 15:33:14 -07:00
}
signalRemoteConnect() {
2022-08-19 20:21:08 -07:00
const iceCandidates = [];
2022-08-10 08:52:43 -07:00
this.peerConnection.addEventListener("icecandidate", (evt) => {
if(evt.candidate) {
2024-12-09 22:29:05 -08:00
if(iceCandidates.length === 0) {
let candidateState = [ 0, 0 ];
let runnable;
setTimeout(runnable = () => {
if(this.peerConnection !== null && this.peerConnection.connectionState !== "disconnected") {
const trial = ++candidateState[1];
if(candidateState[0] !== iceCandidates.length && trial < 3) {
candidateState[0] = iceCandidates.length;
setTimeout(runnable, 2000);
return;
}
this.iceCandidateHandler(JSON.stringify(iceCandidates));
iceCandidates.length = 0;
}
}, 2000);
}
2022-08-19 20:21:08 -07:00
iceCandidates.push({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate });
2022-08-10 08:52:43 -07:00
}
});
2022-08-19 20:21:08 -07:00
this.dataChannel = this.peerConnection.createDataChannel("lan");
this.dataChannel.binaryType = "arraybuffer";
2022-08-19 20:21:08 -07:00
this.dataChannel.addEventListener("open", async (evt) => {
while(iceCandidates.length > 0) {
2024-12-09 22:29:05 -08:00
await new Promise(resolve => setTimeout(resolve, 10));
2022-08-19 20:21:08 -07:00
}
2024-12-08 23:43:32 -08:00
this.remoteDataChannelHandler(this.dataChannel);
});
2022-08-19 20:21:08 -07:00
this.dataChannel.addEventListener("message", (evt) => {
2024-12-08 23:43:32 -08:00
this.remotePacketHandler(evt.data);
2022-08-19 17:40:48 -07:00
}, false);
2022-08-10 08:52:43 -07:00
this.peerConnection.createOffer((desc) => {
const selfDesc = desc;
2024-12-08 23:43:32 -08:00
this.peerConnection.setLocalDescription(selfDesc, () => {
this.descriptionHandler(JSON.stringify(selfDesc));
2022-08-10 08:52:43 -07:00
}, (err) => {
console.error("Failed to set local description! " + err);
2024-12-08 23:43:32 -08:00
this.readyState = READYSTATE_FAILED;
this.signalRemoteDisconnect(false);
2022-08-10 08:52:43 -07:00
});
}, (err) => {
console.error("Failed to set create offer! " + err);
2024-12-08 23:43:32 -08:00
this.readyState = READYSTATE_FAILED;
this.signalRemoteDisconnect(false);
2022-08-10 08:52:43 -07:00
});
this.peerConnection.addEventListener("connectionstatechange", (evt) => {
2024-12-08 23:43:32 -08:00
if(this.peerConnection.connectionState === 'disconnected') {
this.signalRemoteDisconnect(false);
} else if (this.peerConnection.connectionState === 'connected') {
this.readyState = READYSTATE_CONNECTED;
} else if (this.peerConnection.connectionState === 'failed') {
this.readyState = READYSTATE_FAILED;
this.signalRemoteDisconnect(false);
2022-08-10 08:52:43 -07:00
}
});
2022-08-06 15:33:14 -07:00
}
signalRemoteDescription(descJSON) {
try {
this.peerConnection.setRemoteDescription(JSON.parse(descJSON));
} catch (e) {
console.error(e);
this.readyState = READYSTATE_FAILED;
2022-08-19 20:21:08 -07:00
this.signalRemoteDisconnect(false);
}
2022-08-06 15:33:14 -07:00
}
2022-08-19 20:21:08 -07:00
signalRemoteICECandidate(candidates) {
try {
2022-08-19 20:21:08 -07:00
const candidateList = JSON.parse(candidates);
for (let candidate of candidateList) {
this.peerConnection.addIceCandidate(candidate);
}
} catch (e) {
console.error(e);
this.readyState = READYSTATE_FAILED;
2022-08-19 20:21:08 -07:00
this.signalRemoteDisconnect(false);
}
2022-08-10 08:52:43 -07:00
}
2022-08-19 20:21:08 -07:00
signalRemoteDisconnect(quiet) {
2024-12-08 23:43:32 -08:00
if(this.dataChannel !== null) {
2022-08-10 08:52:43 -07:00
this.dataChannel.close();
this.dataChannel = null;
}
2024-12-08 23:43:32 -08:00
if(this.peerConnection !== null) {
this.peerConnection.close();
}
2022-08-19 20:21:08 -07:00
if(!quiet) this.remoteDisconnectHandler();
2022-08-10 08:52:43 -07:00
this.readyState = READYSTATE_DISCONNECTED;
2022-08-06 15:33:14 -07:00
}
};
window.constructLANClient = () => new EaglercraftLANClient();
});
window.startLANClient = () => {
if(typeof window.constructLANClient !== "function") {
window.initializeLANClient();
}
return window.constructLANClient();
};
// %%%%%%%%%%%%%%%%%%%%%%%%%%%%% LAN SERVER CODE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
window.initializeLANServer = (() => {
2022-08-10 08:52:43 -07:00
class EaglercraftLANPeer {
constructor(client, peerId, peerConnection) {
this.client = client;
this.peerId = peerId;
this.peerConnection = peerConnection;
this.dataChannel = null;
2022-08-19 20:21:08 -07:00
const iceCandidates = [];
2024-12-10 21:08:10 -08:00
let hasICE = false;
2022-08-19 20:21:08 -07:00
2022-08-10 08:52:43 -07:00
this.peerConnection.addEventListener("icecandidate", (evt) => {
if(evt.candidate) {
2024-12-09 22:29:05 -08:00
if(iceCandidates.length === 0) {
let candidateState = [ 0, 0 ];
let runnable;
setTimeout(runnable = () => {
if(this.peerConnection !== null && this.peerConnection.connectionState !== "disconnected") {
const trial = ++candidateState[1];
if(candidateState[0] !== iceCandidates.length && trial < 3) {
candidateState[0] = iceCandidates.length;
setTimeout(runnable, 2000);
return;
}
2024-12-10 21:08:10 -08:00
this.client.iceCandidateHandler(this.peerId, JSON.stringify(iceCandidates));
2024-12-09 22:29:05 -08:00
iceCandidates.length = 0;
2024-12-10 21:08:10 -08:00
hasICE = true;
2024-12-09 22:29:05 -08:00
}
}, 2000);
}
2022-08-19 20:21:08 -07:00
iceCandidates.push({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate });
2022-08-10 08:52:43 -07:00
}
});
2022-08-19 20:21:08 -07:00
this.peerConnection.addEventListener("datachannel", async (evt) => {
2024-12-10 21:08:10 -08:00
while(!hasICE) {
2024-12-09 22:29:05 -08:00
await new Promise(resolve => setTimeout(resolve, 10));
2022-08-19 20:21:08 -07:00
}
2024-12-08 23:43:32 -08:00
this.dataChannel = evt.channel;
this.client.remoteClientDataChannelHandler(this.peerId, this.dataChannel);
this.dataChannel.addEventListener("message", (evt) => {
this.client.remoteClientPacketHandler(this.peerId, evt.data);
2022-08-19 17:40:48 -07:00
}, false);
2022-08-19 20:21:08 -07:00
}, false);
2022-08-10 08:52:43 -07:00
this.peerConnection.addEventListener("connectionstatechange", (evt) => {
2024-12-08 23:43:32 -08:00
if(this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed') {
this.client.signalRemoteDisconnect(this.peerId);
2022-08-10 08:52:43 -07:00
}
});
}
disconnect() {
2024-12-08 23:43:32 -08:00
if(this.dataChannel !== null) {
2022-08-10 08:52:43 -07:00
this.dataChannel.close();
this.dataChannel = null;
}
this.peerConnection.close();
}
setRemoteDescription(descJSON) {
try {
const remoteDesc = JSON.parse(descJSON);
this.peerConnection.setRemoteDescription(remoteDesc, () => {
2024-12-08 23:43:32 -08:00
if(remoteDesc.type === 'offer') {
this.peerConnection.createAnswer((desc) => {
2022-08-10 08:52:43 -07:00
const selfDesc = desc;
2024-12-08 23:43:32 -08:00
this.peerConnection.setLocalDescription(selfDesc, () => {
this.client.descriptionHandler(this.peerId, JSON.stringify(selfDesc));
2022-08-10 08:52:43 -07:00
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to set local description for \"" + this.peerId + "\"! " + err);
this.client.signalRemoteDisconnect(this.peerId);
2022-08-10 08:52:43 -07:00
});
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to create answer for \"" + this.peerId + "\"! " + err);
this.client.signalRemoteDisconnect(this.peerId);
2022-08-10 08:52:43 -07:00
});
}
}, (err) => {
2024-12-08 23:43:32 -08:00
console.error("Failed to set remote description for \"" + this.peerId + "\"! " + err);
this.client.signalRemoteDisconnect(this.peerId);
2022-08-10 08:52:43 -07:00
});
} catch (err) {
2024-12-08 23:43:32 -08:00
console.error("Failed to parse remote description for \"" + this.peerId + "\"! " + err);
this.client.signalRemoteDisconnect(this.peerId);
2022-08-10 08:52:43 -07:00
}
}
2022-08-19 20:21:08 -07:00
addICECandidate(candidates) {
2022-08-10 08:52:43 -07:00
try {
2022-08-19 20:21:08 -07:00
const candidateList = JSON.parse(candidates);
for (let candidate of candidateList) {
this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
}
2022-08-10 08:52:43 -07:00
} catch (err) {
console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err);
this.client.signalRemoteDisconnect(this.peerId);
}
}
}
2022-08-06 15:33:14 -07:00
class EaglercraftLANServer {
constructor() {
2022-08-10 08:52:43 -07:00
this.ICEServers = [];
this.hasInit = false;
this.peerList = new Map();
this.iceCandidateHandler = null;
this.descriptionHandler = null;
this.remoteClientDataChannelHandler = null;
this.remoteClientDisconnectHandler = null;
this.remoteClientPacketHandler = null;
2022-08-06 15:33:14 -07:00
}
LANServerSupported() {
2022-08-10 08:52:43 -07:00
return typeof window.RTCPeerConnection !== "undefined";
2022-08-06 15:33:14 -07:00
}
initializeServer() {
2022-08-10 08:52:43 -07:00
// nothing to do!
2022-08-06 15:33:14 -07:00
}
setICEServers(urls) {
2022-08-10 08:52:43 -07:00
this.ICEServers.length = 0;
for(var i = 0; i < urls.length; ++i) {
var etr = urls[i].split(";");
2024-12-08 23:43:32 -08:00
if(etr.length === 1) {
2022-08-10 08:52:43 -07:00
this.ICEServers.push({ urls: etr[0] });
2024-12-08 23:43:32 -08:00
}else if(etr.length === 3) {
2022-08-10 08:52:43 -07:00
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
}
}
2022-08-06 15:33:14 -07:00
}
setICECandidateHandler(cb) {
2022-08-10 08:52:43 -07:00
this.iceCandidateHandler = cb;
2022-08-06 15:33:14 -07:00
}
setDescriptionHandler(cb) {
2022-08-10 08:52:43 -07:00
this.descriptionHandler = cb;
2022-08-06 15:33:14 -07:00
}
setRemoteClientDataChannelHandler(cb) {
2022-08-10 08:52:43 -07:00
this.remoteClientDataChannelHandler = cb;
2022-08-06 15:33:14 -07:00
}
setRemoteClientDisconnectHandler(cb) {
2022-08-10 08:52:43 -07:00
this.remoteClientDisconnectHandler = cb;
2022-08-06 15:33:14 -07:00
}
setRemoteClientPacketHandler(cb) {
2022-08-10 08:52:43 -07:00
this.remoteClientPacketHandler = cb;
2022-08-06 15:33:14 -07:00
}
sendPacketToRemoteClient(peerId, buffer) {
2022-08-10 08:52:43 -07:00
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
2024-12-08 23:43:32 -08:00
if(thePeer.dataChannel != null && thePeer.dataChannel.readyState === "open") {
2022-08-19 20:21:08 -07:00
thePeer.dataChannel.send(buffer);
}else {
this.signalRemoteDisconnect(peerId);
}
2022-08-10 08:52:43 -07:00
}
2022-08-06 15:33:14 -07:00
}
2022-08-10 08:52:43 -07:00
2022-08-06 15:33:14 -07:00
signalRemoteConnect(peerId) {
2022-08-10 08:52:43 -07:00
try {
const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
const peerInstance = new EaglercraftLANPeer(this, peerId, peerConnection);
this.peerList.set(peerId, peerInstance);
} catch (e) {
}
2022-08-06 15:33:14 -07:00
}
2022-08-10 08:52:43 -07:00
2022-08-06 15:33:14 -07:00
signalRemoteDescription(peerId, descJSON) {
2022-08-10 08:52:43 -07:00
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
thePeer.setRemoteDescription(descJSON);
}
2022-08-06 15:33:14 -07:00
}
2022-08-10 08:52:43 -07:00
2022-08-06 15:33:14 -07:00
signalRemoteICECandidate(peerId, candidate) {
2022-08-10 08:52:43 -07:00
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
thePeer.addICECandidate(candidate);
}
2022-08-06 15:33:14 -07:00
}
2022-08-10 08:52:43 -07:00
signalRemoteDisconnect(peerId) {
2024-12-08 23:43:32 -08:00
if(peerId.length === 0) {
2022-08-10 08:52:43 -07:00
for(const thePeer of this.peerList.values()) {
if((typeof thePeer !== "undefined") && thePeer !== null) {
this.peerList.delete(peerId);
2022-08-10 08:52:43 -07:00
try {
thePeer.disconnect();
}catch(e) {}
this.remoteClientDisconnectHandler(peerId);
2022-08-10 08:52:43 -07:00
}
}
this.peerList.clear();
return;
}
var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) {
this.peerList.delete(peerId);
2022-08-10 08:52:43 -07:00
try {
thePeer.disconnect();
}catch(e) {}
this.remoteClientDisconnectHandler(peerId);
2022-08-10 08:52:43 -07:00
}
2022-08-06 15:33:14 -07:00
}
countPeers() {
return this.peerList.size;
}
2022-08-06 15:33:14 -07:00
};
window.constructLANServer = () => new EaglercraftLANServer();
});
window.startLANServer = () => {
if(typeof window.constructLANServer !== "function") {
window.initializeLANServer();
}
return window.constructLANServer();
2022-08-06 15:33:14 -07:00
};