This commit is contained in:
ayunami2000 2024-12-09 04:27:03 -05:00
commit 334c565fee
5 changed files with 184 additions and 310 deletions

View File

@ -19,10 +19,6 @@ window.initializeVoiceClient = (() => {
const READYSTATE_ABORTED = -1; const READYSTATE_ABORTED = -1;
const READYSTATE_DEVICE_INITIALIZED = 1; const READYSTATE_DEVICE_INITIALIZED = 1;
const PEERSTATE_FAILED = 0;
const PEERSTATE_SUCCESS = 1;
const PEERSTATE_LOADING = 2;
class EaglercraftVoicePeer { class EaglercraftVoicePeer {
constructor(client, peerId, peerConnection, offer) { constructor(client, peerId, peerConnection, offer) {
@ -31,52 +27,43 @@ window.initializeVoiceClient = (() => {
this.peerConnection = peerConnection; this.peerConnection = peerConnection;
this.stream = null; this.stream = null;
const self = this;
this.peerConnection.addEventListener("icecandidate", (evt) => { this.peerConnection.addEventListener("icecandidate", (evt) => {
if(evt.candidate) { if(evt.candidate) {
self.client.iceCandidateHandler(self.peerId, JSON.stringify({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate })); this.client.iceCandidateHandler(this.peerId, JSON.stringify({ sdpMLineIndex: evt.candidate.sdpMLineIndex, candidate: evt.candidate.candidate }));
} }
}); });
this.peerConnection.addEventListener("track", (evt) => { this.peerConnection.addEventListener("track", (evt) => {
self.rawStream = evt.streams[0]; this.rawStream = evt.streams[0];
const aud = new Audio(); const aud = new Audio();
aud.autoplay = true; aud.autoplay = true;
aud.muted = true; aud.muted = true;
aud.onended = function() { aud.onended = function() {
aud.remove(); aud.remove();
}; };
aud.srcObject = self.rawStream; aud.srcObject = this.rawStream;
self.client.peerTrackHandler(self.peerId, self.rawStream); this.client.peerTrackHandler(this.peerId, this.rawStream);
}); });
this.peerConnection.addStream(this.client.localMediaStream.stream); this.peerConnection.addStream(this.client.localMediaStream.stream);
if (offer) { if (offer) {
this.peerConnection.createOffer((desc) => { this.peerConnection.createOffer((desc) => {
const selfDesc = desc; const selfDesc = desc;
self.peerConnection.setLocalDescription(selfDesc, () => { this.peerConnection.setLocalDescription(selfDesc, () => {
self.client.descriptionHandler(self.peerId, JSON.stringify(selfDesc)); this.client.descriptionHandler(this.peerId, JSON.stringify(selfDesc));
if (self.client.peerStateInitial != PEERSTATE_SUCCESS) self.client.peerStateInitial = PEERSTATE_SUCCESS;
}, (err) => { }, (err) => {
console.error("Failed to set local description for \"" + self.peerId + "\"! " + err); console.error("Failed to set local description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateInitial == PEERSTATE_LOADING) self.client.peerStateInitial = PEERSTATE_FAILED; this.client.signalDisconnect(this.peerId);
self.client.signalDisconnect(self.peerId);
}); });
}, (err) => { }, (err) => {
console.error("Failed to set create offer for \"" + self.peerId + "\"! " + err); console.error("Failed to set create offer for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateInitial == PEERSTATE_LOADING) self.client.peerStateInitial = PEERSTATE_FAILED; this.client.signalDisconnect(this.peerId);
self.client.signalDisconnect(self.peerId);
}); });
} }
this.peerConnection.addEventListener("connectionstatechange", (evt) => { this.peerConnection.addEventListener("connectionstatechange", (evt) => {
if(self.peerConnection.connectionState === 'disconnected') { if(this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed') {
self.client.signalDisconnect(self.peerId); this.client.signalDisconnect(this.peerId);
} else if (self.peerConnection.connectionState === 'connected') {
if (self.client.peerState != PEERSTATE_SUCCESS) self.client.peerState = PEERSTATE_SUCCESS;
} else if (self.peerConnection.connectionState === 'failed') {
if (self.client.peerState == PEERSTATE_LOADING) self.client.peerState = PEERSTATE_FAILED;
self.client.signalDisconnect(self.peerId);
} }
}); });
@ -91,46 +78,38 @@ window.initializeVoiceClient = (() => {
} }
setRemoteDescription(descJSON) { setRemoteDescription(descJSON) {
const self = this;
try { try {
const remoteDesc = JSON.parse(descJSON); const remoteDesc = JSON.parse(descJSON);
this.peerConnection.setRemoteDescription(remoteDesc, () => { this.peerConnection.setRemoteDescription(remoteDesc, () => {
if(remoteDesc.type == 'offer') { if(remoteDesc.type === 'offer') {
self.peerConnection.createAnswer((desc) => { this.peerConnection.createAnswer((desc) => {
const selfDesc = desc; const selfDesc = desc;
self.peerConnection.setLocalDescription(selfDesc, () => { this.peerConnection.setLocalDescription(selfDesc, () => {
self.client.descriptionHandler(self.peerId, JSON.stringify(selfDesc)); this.client.descriptionHandler(this.peerId, JSON.stringify(selfDesc));
if (self.client.peerStateDesc != PEERSTATE_SUCCESS) self.client.peerStateDesc = PEERSTATE_SUCCESS;
}, (err) => { }, (err) => {
console.error("Failed to set local description for \"" + self.peerId + "\"! " + err); console.error("Failed to set local description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalDisconnect(this.peerId);
self.client.signalDisconnect(self.peerId);
}); });
}, (err) => { }, (err) => {
console.error("Failed to create answer for \"" + self.peerId + "\"! " + err); console.error("Failed to create answer for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalDisconnect(this.peerId);
self.client.signalDisconnect(self.peerId);
}); });
} }
}, (err) => { }, (err) => {
console.error("Failed to set remote description for \"" + self.peerId + "\"! " + err); console.error("Failed to set remote description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalDisconnect(this.peerId);
self.client.signalDisconnect(self.peerId);
}); });
} catch (err) { } catch (err) {
console.error("Failed to parse remote description for \"" + self.peerId + "\"! " + err); console.error("Failed to parse remote description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalDisconnect(this.peerId);
self.client.signalDisconnect(self.peerId);
} }
} }
addICECandidate(candidate) { addICECandidate(candidate) {
try { try {
this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(candidate))); this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(candidate)));
if (this.client.peerStateIce != PEERSTATE_SUCCESS) this.client.peerStateIce = PEERSTATE_SUCCESS;
} catch (err) { } catch (err) {
console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err); console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err);
if (this.client.peerStateIce == PEERSTATE_LOADING) this.client.peerStateIce = PEERSTATE_FAILED;
this.client.signalDisconnect(this.peerId); this.client.signalDisconnect(this.peerId);
} }
} }
@ -144,11 +123,6 @@ window.initializeVoiceClient = (() => {
this.hasInit = false; this.hasInit = false;
this.peerList = new Map(); this.peerList = new Map();
this.readyState = READYSTATE_NONE; this.readyState = READYSTATE_NONE;
this.peerState = PEERSTATE_LOADING;
this.peerStateConnect = PEERSTATE_LOADING;
this.peerStateInitial = PEERSTATE_LOADING;
this.peerStateDesc = PEERSTATE_LOADING;
this.peerStateIce = PEERSTATE_LOADING;
this.iceCandidateHandler = null; this.iceCandidateHandler = null;
this.descriptionHandler = null; this.descriptionHandler = null;
this.peerTrackHandler = null; this.peerTrackHandler = null;
@ -165,9 +139,9 @@ window.initializeVoiceClient = (() => {
this.ICEServers.length = 0; this.ICEServers.length = 0;
for(var i = 0; i < urls.length; ++i) { for(var i = 0; i < urls.length; ++i) {
var etr = urls[i].split(";"); var etr = urls[i].split(";");
if(etr.length == 1) { if(etr.length === 1) {
this.ICEServers.push({ urls: etr[0] }); this.ICEServers.push({ urls: etr[0] });
}else if(etr.length == 3) { }else if(etr.length === 3) {
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] }); this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
} }
} }
@ -195,21 +169,20 @@ window.initializeVoiceClient = (() => {
initializeDevices() { initializeDevices() {
if(!this.hasInit) { if(!this.hasInit) {
const self = this;
navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => { navigator.mediaDevices.getUserMedia({ audio: true, video: false }).then((stream) => {
self.microphoneVolumeAudioContext = new AudioContext(); this.microphoneVolumeAudioContext = new AudioContext();
self.localRawMediaStream = stream; this.localRawMediaStream = stream;
self.localRawMediaStream.getAudioTracks()[0].enabled = false; this.localRawMediaStream.getAudioTracks()[0].enabled = false;
self.localMediaStream = self.microphoneVolumeAudioContext.createMediaStreamDestination(); this.localMediaStream = this.microphoneVolumeAudioContext.createMediaStreamDestination();
self.localMediaStreamGain = self.microphoneVolumeAudioContext.createGain(); this.localMediaStreamGain = this.microphoneVolumeAudioContext.createGain();
var localStreamIn = self.microphoneVolumeAudioContext.createMediaStreamSource(stream); var localStreamIn = this.microphoneVolumeAudioContext.createMediaStreamSource(stream);
localStreamIn.connect(self.localMediaStreamGain); localStreamIn.connect(this.localMediaStreamGain);
self.localMediaStreamGain.connect(self.localMediaStream); this.localMediaStreamGain.connect(this.localMediaStream);
self.localMediaStreamGain.gain.value = 1.0; this.localMediaStreamGain.gain.value = 1.0;
self.readyState = READYSTATE_DEVICE_INITIALIZED; this.readyState = READYSTATE_DEVICE_INITIALIZED;
this.hasInit = true; this.hasInit = true;
}).catch((err) => { }).catch((err) => {
self.readyState = READYSTATE_ABORTED; this.readyState = READYSTATE_ABORTED;
}); });
}else { }else {
this.readyState = READYSTATE_DEVICE_INITIALIZED; this.readyState = READYSTATE_DEVICE_INITIALIZED;
@ -224,44 +197,17 @@ window.initializeVoiceClient = (() => {
this.localMediaStreamGain.gain.value = val * 2.0; this.localMediaStreamGain.gain.value = val * 2.0;
} }
} }
resetPeerStates() {
this.peerState = this.peerStateConnect = this.peerStateInitial = this.peerStateDesc = this.peerStateIce = PEERSTATE_LOADING;
}
getPeerState() {
return this.peerState;
}
getPeerStateConnect() {
return this.peerStateConnect;
}
getPeerStateInitial() {
return this.peerStateInitial;
}
getPeerStateDesc() {
return this.peerStateDesc;
}
getPeerStateIce() {
return this.peerStateIce;
}
getReadyState() { getReadyState() {
return this.readyState; return this.readyState;
} }
signalConnect(peerId, offer) { signalConnect(peerId, offer) {
if (!this.hasInit) this.initializeDevices();
try { try {
const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] }); const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
const peerInstance = new EaglercraftVoicePeer(this, peerId, peerConnection, offer); const peerInstance = new EaglercraftVoicePeer(this, peerId, peerConnection, offer);
this.peerList.set(peerId, peerInstance); this.peerList.set(peerId, peerInstance);
if (this.peerStateConnect != PEERSTATE_SUCCESS) this.peerStateConnect = PEERSTATE_SUCCESS;
} catch (e) { } catch (e) {
if (this.peerStateConnect == PEERSTATE_LOADING) this.peerStateConnect = PEERSTATE_FAILED;
} }
} }
@ -341,11 +287,11 @@ window.initializeLANClient = (() => {
initializeClient() { initializeClient() {
try { try {
if(this.dataChannel != null) { if(this.dataChannel !== null) {
this.dataChannel.close(); this.dataChannel.close();
this.dataChannel = null; this.dataChannel = null;
} }
if(this.peerConnection != null) { if(this.peerConnection !== null) {
this.peerConnection.close(); this.peerConnection.close();
} }
this.peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] }); this.peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
@ -359,9 +305,9 @@ window.initializeLANClient = (() => {
this.ICEServers.length = 0; this.ICEServers.length = 0;
for(var i = 0; i < urls.length; ++i) { for(var i = 0; i < urls.length; ++i) {
var etr = urls[i].split(";"); var etr = urls[i].split(";");
if(etr.length == 1) { if(etr.length === 1) {
this.ICEServers.push({ urls: etr[0] }); this.ICEServers.push({ urls: etr[0] });
}else if(etr.length == 3) { }else if(etr.length === 3) {
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] }); this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
} }
} }
@ -392,7 +338,7 @@ window.initializeLANClient = (() => {
} }
sendPacketToServer(buffer) { sendPacketToServer(buffer) {
if(this.dataChannel != null && this.dataChannel.readyState == "open") { if(this.dataChannel !== null && this.dataChannel.readyState === "open") {
this.dataChannel.send(buffer); this.dataChannel.send(buffer);
}else { }else {
this.signalRemoteDisconnect(false); this.signalRemoteDisconnect(false);
@ -400,15 +346,14 @@ window.initializeLANClient = (() => {
} }
signalRemoteConnect() { signalRemoteConnect() {
const self = this;
const iceCandidates = []; const iceCandidates = [];
this.peerConnection.addEventListener("icecandidate", (evt) => { this.peerConnection.addEventListener("icecandidate", (evt) => {
if(evt.candidate) { if(evt.candidate) {
if(iceCandidates.length == 0) setTimeout(() => { if(iceCandidates.length === 0) setTimeout(() => {
if(self.peerConnection != null && self.peerConnection.connectionState != "disconnected") { if(this.peerConnection !== null && this.peerConnection.connectionState !== "disconnected") {
self.iceCandidateHandler(JSON.stringify(iceCandidates)); this.iceCandidateHandler(JSON.stringify(iceCandidates));
iceCandidates.length = 0; iceCandidates.length = 0;
} }
}, 3000); }, 3000);
@ -423,36 +368,36 @@ window.initializeLANClient = (() => {
while(iceCandidates.length > 0) { while(iceCandidates.length > 0) {
await new Promise(resolve => setTimeout(resolve, 0)); await new Promise(resolve => setTimeout(resolve, 0));
} }
self.remoteDataChannelHandler(self.dataChannel); this.remoteDataChannelHandler(this.dataChannel);
}); });
this.dataChannel.addEventListener("message", (evt) => { this.dataChannel.addEventListener("message", (evt) => {
self.remotePacketHandler(evt.data); this.remotePacketHandler(evt.data);
}, false); }, false);
this.peerConnection.createOffer((desc) => { this.peerConnection.createOffer((desc) => {
const selfDesc = desc; const selfDesc = desc;
self.peerConnection.setLocalDescription(selfDesc, () => { this.peerConnection.setLocalDescription(selfDesc, () => {
self.descriptionHandler(JSON.stringify(selfDesc)); this.descriptionHandler(JSON.stringify(selfDesc));
}, (err) => { }, (err) => {
console.error("Failed to set local description! " + err); console.error("Failed to set local description! " + err);
self.readyState = READYSTATE_FAILED; this.readyState = READYSTATE_FAILED;
self.signalRemoteDisconnect(false); this.signalRemoteDisconnect(false);
}); });
}, (err) => { }, (err) => {
console.error("Failed to set create offer! " + err); console.error("Failed to set create offer! " + err);
self.readyState = READYSTATE_FAILED; this.readyState = READYSTATE_FAILED;
self.signalRemoteDisconnect(false); this.signalRemoteDisconnect(false);
}); });
this.peerConnection.addEventListener("connectionstatechange", (evt) => { this.peerConnection.addEventListener("connectionstatechange", (evt) => {
if(self.peerConnection.connectionState === 'disconnected') { if(this.peerConnection.connectionState === 'disconnected') {
self.signalRemoteDisconnect(false); this.signalRemoteDisconnect(false);
} else if (self.peerConnection.connectionState === 'connected') { } else if (this.peerConnection.connectionState === 'connected') {
self.readyState = READYSTATE_CONNECTED; this.readyState = READYSTATE_CONNECTED;
} else if (self.peerConnection.connectionState === 'failed') { } else if (this.peerConnection.connectionState === 'failed') {
self.readyState = READYSTATE_FAILED; this.readyState = READYSTATE_FAILED;
self.signalRemoteDisconnect(false); this.signalRemoteDisconnect(false);
} }
}); });
} }
@ -481,11 +426,11 @@ window.initializeLANClient = (() => {
} }
signalRemoteDisconnect(quiet) { signalRemoteDisconnect(quiet) {
if(this.dataChannel != null) { if(this.dataChannel !== null) {
this.dataChannel.close(); this.dataChannel.close();
this.dataChannel = null; this.dataChannel = null;
} }
if(this.peerConnection != null) { if(this.peerConnection !== null) {
this.peerConnection.close(); this.peerConnection.close();
} }
if(!quiet) this.remoteDisconnectHandler(); if(!quiet) this.remoteDisconnectHandler();
@ -510,10 +455,6 @@ window.startLANClient = () => {
window.initializeLANServer = (() => { window.initializeLANServer = (() => {
const PEERSTATE_FAILED = 0;
const PEERSTATE_SUCCESS = 1;
const PEERSTATE_LOADING = 2;
class EaglercraftLANPeer { class EaglercraftLANPeer {
constructor(client, peerId, peerConnection) { constructor(client, peerId, peerConnection) {
@ -522,15 +463,13 @@ window.initializeLANServer = (() => {
this.peerConnection = peerConnection; this.peerConnection = peerConnection;
this.dataChannel = null; this.dataChannel = null;
const self = this;
const iceCandidates = []; const iceCandidates = [];
this.peerConnection.addEventListener("icecandidate", (evt) => { this.peerConnection.addEventListener("icecandidate", (evt) => {
if(evt.candidate) { if(evt.candidate) {
if(iceCandidates.length == 0) setTimeout(() => { if(iceCandidates.length === 0) setTimeout(() => {
if(self.peerConnection != null && self.peerConnection.connectionState != "disconnected") { if(this.peerConnection !== null && this.peerConnection.connectionState !== "disconnected") {
self.client.iceCandidateHandler(self.peerId, JSON.stringify(iceCandidates)); this.client.iceCandidateHandler(this.peerId, JSON.stringify(iceCandidates));
iceCandidates.length = 0; iceCandidates.length = 0;
} }
}, 3000); }, 3000);
@ -542,28 +481,23 @@ window.initializeLANServer = (() => {
while(iceCandidates.length > 0) { while(iceCandidates.length > 0) {
await new Promise(resolve => setTimeout(resolve, 0)); await new Promise(resolve => setTimeout(resolve, 0));
} }
self.dataChannel = evt.channel; this.dataChannel = evt.channel;
self.client.remoteClientDataChannelHandler(self.peerId, self.dataChannel); this.client.remoteClientDataChannelHandler(this.peerId, this.dataChannel);
self.dataChannel.addEventListener("message", (evt) => { this.dataChannel.addEventListener("message", (evt) => {
self.client.remoteClientPacketHandler(self.peerId, evt.data); this.client.remoteClientPacketHandler(this.peerId, evt.data);
}, false); }, false);
}, false); }, false);
this.peerConnection.addEventListener("connectionstatechange", (evt) => { this.peerConnection.addEventListener("connectionstatechange", (evt) => {
if(self.peerConnection.connectionState === 'disconnected') { if(this.peerConnection.connectionState === 'disconnected' || this.peerConnection.connectionState === 'failed') {
self.client.signalRemoteDisconnect(self.peerId); this.client.signalRemoteDisconnect(this.peerId);
} else if (self.peerConnection.connectionState === 'connected') {
if (self.client.peerState != PEERSTATE_SUCCESS) self.client.peerState = PEERSTATE_SUCCESS;
} else if (self.peerConnection.connectionState === 'failed') {
if (self.client.peerState == PEERSTATE_LOADING) self.client.peerState = PEERSTATE_FAILED;
self.client.signalRemoteDisconnect(self.peerId);
} }
}); });
} }
disconnect() { disconnect() {
if(this.dataChannel != null) { if(this.dataChannel !== null) {
this.dataChannel.close(); this.dataChannel.close();
this.dataChannel = null; this.dataChannel = null;
} }
@ -571,36 +505,30 @@ window.initializeLANServer = (() => {
} }
setRemoteDescription(descJSON) { setRemoteDescription(descJSON) {
const self = this;
try { try {
const remoteDesc = JSON.parse(descJSON); const remoteDesc = JSON.parse(descJSON);
this.peerConnection.setRemoteDescription(remoteDesc, () => { this.peerConnection.setRemoteDescription(remoteDesc, () => {
if(remoteDesc.type == 'offer') { if(remoteDesc.type === 'offer') {
self.peerConnection.createAnswer((desc) => { this.peerConnection.createAnswer((desc) => {
const selfDesc = desc; const selfDesc = desc;
self.peerConnection.setLocalDescription(selfDesc, () => { this.peerConnection.setLocalDescription(selfDesc, () => {
self.client.descriptionHandler(self.peerId, JSON.stringify(selfDesc)); this.client.descriptionHandler(this.peerId, JSON.stringify(selfDesc));
if (self.client.peerStateDesc != PEERSTATE_SUCCESS) self.client.peerStateDesc = PEERSTATE_SUCCESS;
}, (err) => { }, (err) => {
console.error("Failed to set local description for \"" + self.peerId + "\"! " + err); console.error("Failed to set local description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalRemoteDisconnect(this.peerId);
self.client.signalRemoteDisconnect(self.peerId);
}); });
}, (err) => { }, (err) => {
console.error("Failed to create answer for \"" + self.peerId + "\"! " + err); console.error("Failed to create answer for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalRemoteDisconnect(this.peerId);
self.client.signalRemoteDisconnect(self.peerId);
}); });
} }
}, (err) => { }, (err) => {
console.error("Failed to set remote description for \"" + self.peerId + "\"! " + err); console.error("Failed to set remote description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalRemoteDisconnect(this.peerId);
self.client.signalRemoteDisconnect(self.peerId);
}); });
} catch (err) { } catch (err) {
console.error("Failed to parse remote description for \"" + self.peerId + "\"! " + err); console.error("Failed to parse remote description for \"" + this.peerId + "\"! " + err);
if (self.client.peerStateDesc == PEERSTATE_LOADING) self.client.peerStateDesc = PEERSTATE_FAILED; this.client.signalRemoteDisconnect(this.peerId);
self.client.signalRemoteDisconnect(self.peerId);
} }
} }
@ -610,10 +538,8 @@ window.initializeLANServer = (() => {
for (let candidate of candidateList) { for (let candidate of candidateList) {
this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate)); this.peerConnection.addIceCandidate(new RTCIceCandidate(candidate));
} }
if (this.client.peerStateIce != PEERSTATE_SUCCESS) this.client.peerStateIce = PEERSTATE_SUCCESS;
} catch (err) { } catch (err) {
console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err); console.error("Failed to parse ice candidate for \"" + this.peerId + "\"! " + err);
if (this.client.peerStateIce == PEERSTATE_LOADING) this.client.peerStateIce = PEERSTATE_FAILED;
this.client.signalRemoteDisconnect(this.peerId); this.client.signalRemoteDisconnect(this.peerId);
} }
} }
@ -626,11 +552,6 @@ window.initializeLANServer = (() => {
this.ICEServers = []; this.ICEServers = [];
this.hasInit = false; this.hasInit = false;
this.peerList = new Map(); this.peerList = new Map();
this.peerState = PEERSTATE_LOADING;
this.peerStateConnect = PEERSTATE_LOADING;
this.peerStateInitial = PEERSTATE_LOADING;
this.peerStateDesc = PEERSTATE_LOADING;
this.peerStateIce = PEERSTATE_LOADING;
this.iceCandidateHandler = null; this.iceCandidateHandler = null;
this.descriptionHandler = null; this.descriptionHandler = null;
this.remoteClientDataChannelHandler = null; this.remoteClientDataChannelHandler = null;
@ -650,9 +571,9 @@ window.initializeLANServer = (() => {
this.ICEServers.length = 0; this.ICEServers.length = 0;
for(var i = 0; i < urls.length; ++i) { for(var i = 0; i < urls.length; ++i) {
var etr = urls[i].split(";"); var etr = urls[i].split(";");
if(etr.length == 1) { if(etr.length === 1) {
this.ICEServers.push({ urls: etr[0] }); this.ICEServers.push({ urls: etr[0] });
}else if(etr.length == 3) { }else if(etr.length === 3) {
this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] }); this.ICEServers.push({ urls: etr[0], username: etr[1], credential: etr[2] });
} }
} }
@ -681,7 +602,7 @@ window.initializeLANServer = (() => {
sendPacketToRemoteClient(peerId, buffer) { sendPacketToRemoteClient(peerId, buffer) {
var thePeer = this.peerList.get(peerId); var thePeer = this.peerList.get(peerId);
if((typeof thePeer !== "undefined") && thePeer !== null) { if((typeof thePeer !== "undefined") && thePeer !== null) {
if(thePeer.dataChannel != null && thePeer.dataChannel.readyState == "open") { if(thePeer.dataChannel != null && thePeer.dataChannel.readyState === "open") {
thePeer.dataChannel.send(buffer); thePeer.dataChannel.send(buffer);
}else { }else {
this.signalRemoteDisconnect(peerId); this.signalRemoteDisconnect(peerId);
@ -689,38 +610,12 @@ window.initializeLANServer = (() => {
} }
} }
resetPeerStates() {
this.peerState = this.peerStateConnect = this.peerStateInitial = this.peerStateDesc = this.peerStateIce = PEERSTATE_LOADING;
}
getPeerState() {
return this.peerState;
}
getPeerStateConnect() {
return this.peerStateConnect;
}
getPeerStateInitial() {
return this.peerStateInitial;
}
getPeerStateDesc() {
return this.peerStateDesc;
}
getPeerStateIce() {
return this.peerStateIce;
}
signalRemoteConnect(peerId) { signalRemoteConnect(peerId) {
try { try {
const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] }); const peerConnection = new RTCPeerConnection({ iceServers: this.ICEServers, optional: [ { DtlsSrtpKeyAgreement: true } ] });
const peerInstance = new EaglercraftLANPeer(this, peerId, peerConnection); const peerInstance = new EaglercraftLANPeer(this, peerId, peerConnection);
this.peerList.set(peerId, peerInstance); this.peerList.set(peerId, peerInstance);
if (this.peerStateConnect != PEERSTATE_SUCCESS) this.peerStateConnect = PEERSTATE_SUCCESS;
} catch (e) { } catch (e) {
if (this.peerStateConnect == PEERSTATE_LOADING) this.peerStateConnect = PEERSTATE_FAILED;
} }
} }
@ -739,7 +634,7 @@ window.initializeLANServer = (() => {
} }
signalRemoteDisconnect(peerId) { signalRemoteDisconnect(peerId) {
if(peerId.length == 0) { if(peerId.length === 0) {
for(const thePeer of this.peerList.values()) { for(const thePeer of this.peerList.values()) {
if((typeof thePeer !== "undefined") && thePeer !== null) { if((typeof thePeer !== "undefined") && thePeer !== null) {
this.peerList.delete(peerId); this.peerList.delete(peerId);

View File

@ -176,42 +176,33 @@ public class IntegratedServerLAN {
} }
private static final class LANClient { private static final class LANClient {
private static final int PRE = 0, SENT_ICE_CANDIDATE = 2, SENT_DESCRIPTION = 3, CONNECTED = 4, CLOSED = 5; private static final int PRE = 0, RECEIVED_ICE_CANDIDATE = 1, SENT_ICE_CANDIDATE = 2, RECEIVED_DESCRIPTION = 3,
SENT_DESCRIPTION = 4, RECEIVED_SUCCESS = 5, CONNECTED = 6, CLOSED = 7;
protected final String clientId; protected final String clientId;
protected int state = PRE; protected int state = PRE;
protected boolean dead = false; protected boolean dead = false;
protected String localICECandidate = null;
protected final long startTime;
protected LANClient(String clientId) { protected LANClient(String clientId) {
this.clientId = clientId; this.clientId = clientId;
this.startTime = EaglerAdapter.steadyTimeMillis();
EaglerAdapter.serverLANCreatePeer(clientId); EaglerAdapter.serverLANCreatePeer(clientId);
} }
protected void handleICECandidates(String candidates) { protected void handleICECandidates(String candidates) {
if(state == SENT_DESCRIPTION) { if(state == SENT_DESCRIPTION) {
EaglerAdapter.serverLANPeerICECandidates(clientId, candidates); EaglerAdapter.serverLANPeerICECandidates(clientId, candidates);
long millis = EaglerAdapter.steadyTimeMillis(); if(localICECandidate != null) {
do { lanRelaySocket.writePacket(new IPacket03ICECandidate(clientId, localICECandidate));
LANPeerEvent evt; localICECandidate = null;
if((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) { state = SENT_ICE_CANDIDATE;
if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) { }else {
lanRelaySocket.writePacket(new IPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates)); state = RECEIVED_ICE_CANDIDATE;
state = SENT_ICE_CANDIDATE; }
return;
}else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) {
System.err.println("LAN client '" + clientId + "' disconnected while waiting for server ICE candidates");
}else {
System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName());
}
disconnect();
return;
}
EaglerAdapter.sleep(20);
}while(EaglerAdapter.steadyTimeMillis() - millis < 5000l);
System.err.println("Getting server ICE candidates for '" + clientId + "' timed out!");
disconnect();
}else { }else {
System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket03ICECandidate for '" + clientId + "'"); System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket03ICECandidate for '" + clientId + "'");
} }
@ -220,26 +211,7 @@ public class IntegratedServerLAN {
protected void handleDescription(String description) { protected void handleDescription(String description) {
if(state == PRE) { if(state == PRE) {
EaglerAdapter.serverLANPeerDescription(clientId, description); EaglerAdapter.serverLANPeerDescription(clientId, description);
long millis = EaglerAdapter.steadyTimeMillis(); state = RECEIVED_DESCRIPTION;
do {
LANPeerEvent evt;
if((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) {
if(evt instanceof LANPeerEvent.LANPeerDescriptionEvent) {
lanRelaySocket.writePacket(new IPacket04Description(clientId, ((LANPeerEvent.LANPeerDescriptionEvent)evt).description));
state = SENT_DESCRIPTION;
return;
}else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) {
System.err.println("LAN client '" + clientId + "' disconnected while waiting for server description");
}else {
System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName());
}
disconnect();
return;
}
EaglerAdapter.sleep(20);
}while(EaglerAdapter.steadyTimeMillis() - millis < 5000l);
System.err.println("Getting server description for '" + clientId + "' timed out!");
disconnect();
}else { }else {
System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket04Description for '" + clientId + "'"); System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket04Description for '" + clientId + "'");
} }
@ -247,30 +219,7 @@ public class IntegratedServerLAN {
protected void handleSuccess() { protected void handleSuccess() {
if(state == SENT_ICE_CANDIDATE) { if(state == SENT_ICE_CANDIDATE) {
long millis = EaglerAdapter.steadyTimeMillis(); state = RECEIVED_SUCCESS;
do {
LANPeerEvent evt;
while((evt = EaglerAdapter.serverLANGetEvent(clientId)) != null && evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
// skip ice candidates
}
if(evt != null) {
if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) {
EaglerAdapter.enableChannel("NET|" + clientId);
IntegratedServer.sendIPCPacket(new IPCPacket0CPlayerChannel(clientId, true));
state = CONNECTED;
return;
}else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) {
System.err.println("LAN client '" + clientId + "' disconnected while waiting for connection");
}else {
System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName());
}
disconnect();
return;
}
EaglerAdapter.sleep(20);
}while(EaglerAdapter.steadyTimeMillis() - millis < 5000l);
System.err.println("Getting server description for '" + clientId + "' timed out!");
disconnect();
}else { }else {
System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket05ClientSuccess for '" + clientId + "'"); System.err.println("Relay [" + lanRelaySocket.getURI() + "] unexpected IPacket05ClientSuccess for '" + clientId + "'");
} }
@ -286,23 +235,70 @@ public class IntegratedServerLAN {
} }
protected void update() { protected void update() {
if(state == CONNECTED) { if(state != CLOSED) {
PKT pk; if(state != CONNECTED && EaglerAdapter.steadyTimeMillis() - startTime > 13000l) {
while(state == CONNECTED && (pk = EaglerAdapter.recieveFromIntegratedServer("NET|" + clientId)) != null) { System.out.println("LAN client '" + clientId + "' handshake timed out");
EaglerAdapter.serverLANWritePacket(clientId, pk.data); disconnect();
return;
} }
LANPeerEvent evt; List<LANPeerEvent> l = EaglerAdapter.serverLANGetAllEvent(clientId);
while(state == CONNECTED && (evt = EaglerAdapter.serverLANGetEvent(clientId)) != null) { if(l == null) {
if(evt instanceof LANPeerEvent.LANPeerPacketEvent) { return;
EaglerAdapter.sendToIntegratedServer("NET|" + clientId, ((LANPeerEvent.LANPeerPacketEvent)evt).payload); }
}else if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) { read_loop: for(int i = 0, s = l.size(); i < s; ++i) {
LANPeerEvent evt = l.get(i);
if(evt instanceof LANPeerEvent.LANPeerDisconnectEvent) {
System.out.println("LAN client '" + clientId + "' disconnected"); System.out.println("LAN client '" + clientId + "' disconnected");
disconnect(); disconnect();
}else { }else {
System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName()); switch(state) {
case SENT_DESCRIPTION:{
if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
localICECandidate = ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates;
continue read_loop;
}
}
case RECEIVED_ICE_CANDIDATE: {
if(evt instanceof LANPeerEvent.LANPeerICECandidateEvent) {
lanRelaySocket.writePacket(new IPacket03ICECandidate(clientId, ((LANPeerEvent.LANPeerICECandidateEvent)evt).candidates));
state = SENT_ICE_CANDIDATE;
continue read_loop;
}
}
case RECEIVED_DESCRIPTION: {
if(evt instanceof LANPeerEvent.LANPeerDescriptionEvent) {
lanRelaySocket.writePacket(new IPacket04Description(clientId, ((LANPeerEvent.LANPeerDescriptionEvent)evt).description));
state = SENT_DESCRIPTION;
continue read_loop;
}
}
case SENT_ICE_CANDIDATE:
case RECEIVED_SUCCESS: {
if(evt instanceof LANPeerEvent.LANPeerDataChannelEvent) {
EaglerAdapter.enableChannel("NET|" + clientId);
state = CONNECTED;
continue read_loop;
}
}
case CONNECTED: {
if(evt instanceof LANPeerEvent.LANPeerPacketEvent) {
EaglerAdapter.sendToIntegratedServer("NET|" + clientId, ((LANPeerEvent.LANPeerPacketEvent)evt).payload);
continue read_loop;
}
}
default: {
break;
}
}
if(state != CLOSED) {
System.err.println("LAN client '" + clientId + "' had an accident: " + evt.getClass().getSimpleName());
}
disconnect(); disconnect();
return;
} }
} }
}else {
disconnect();
} }
} }

View File

@ -58,7 +58,6 @@ import org.teavm.jso.dom.html.HTMLVideoElement;
import org.teavm.jso.media.MediaError; import org.teavm.jso.media.MediaError;
import org.teavm.jso.typedarrays.ArrayBuffer; import org.teavm.jso.typedarrays.ArrayBuffer;
import org.teavm.jso.typedarrays.DataView; import org.teavm.jso.typedarrays.DataView;
import org.teavm.jso.typedarrays.Float32Array;
import org.teavm.jso.typedarrays.Int32Array; import org.teavm.jso.typedarrays.Int32Array;
import org.teavm.jso.typedarrays.Uint8Array; import org.teavm.jso.typedarrays.Uint8Array;
import org.teavm.jso.typedarrays.Uint8ClampedArray; import org.teavm.jso.typedarrays.Uint8ClampedArray;
@ -2632,7 +2631,6 @@ public class EaglerAdapterImpl2 {
public static final void enableVoice(Voice.VoiceChannel enable) { public static final void enableVoice(Voice.VoiceChannel enable) {
if (enabledChannel == enable) return; if (enabledChannel == enable) return;
voiceClient.resetPeerStates();
if (enabledChannel == Voice.VoiceChannel.PROXIMITY) { if (enabledChannel == Voice.VoiceChannel.PROXIMITY) {
for (String username : nearbyPlayers) voiceClient.signalDisconnect(username, false); for (String username : nearbyPlayers) voiceClient.signalDisconnect(username, false);
for (String username : recentlyNearbyPlayers) voiceClient.signalDisconnect(username, false); for (String username : recentlyNearbyPlayers) voiceClient.signalDisconnect(username, false);
@ -2752,13 +2750,10 @@ public class EaglerAdapterImpl2 {
public static final Voice.VoiceChannel getVoiceChannel() { public static final Voice.VoiceChannel getVoiceChannel() {
return enabledChannel; return enabledChannel;
} }
public static final boolean voicePeerErrored() {
return voiceClient.getPeerState() == EaglercraftVoiceClient.PEERSTATE_FAILED || voiceClient.getPeerStateConnect() == EaglercraftVoiceClient.PEERSTATE_FAILED || voiceClient.getPeerStateInitial() == EaglercraftVoiceClient.PEERSTATE_FAILED || voiceClient.getPeerStateDesc() == EaglercraftVoiceClient.PEERSTATE_FAILED || voiceClient.getPeerStateIce() == EaglercraftVoiceClient.PEERSTATE_FAILED;
}
public static final Voice.VoiceStatus getVoiceStatus() { public static final Voice.VoiceStatus getVoiceStatus() {
return (!voiceAvailable() || !voiceAllowed()) ? Voice.VoiceStatus.UNAVAILABLE : return (!voiceAvailable() || !voiceAllowed()) ? Voice.VoiceStatus.UNAVAILABLE :
(voiceClient.getReadyState() != EaglercraftVoiceClient.READYSTATE_DEVICE_INITIALIZED ? (voiceClient.getReadyState() != EaglercraftVoiceClient.READYSTATE_DEVICE_INITIALIZED ?
Voice.VoiceStatus.CONNECTING : (voicePeerErrored() ? Voice.VoiceStatus.UNAVAILABLE : Voice.VoiceStatus.CONNECTED)); Voice.VoiceStatus.CONNECTING : Voice.VoiceStatus.CONNECTED);
} }
private static boolean talkStatus = false; private static boolean talkStatus = false;
@ -4258,6 +4253,26 @@ public class EaglerAdapterImpl2 {
return null; return null;
} }
} }
public static final List<LANPeerEvent> serverLANGetAllEvent(String clientId) {
if(serverLANEventBuffer.size() > 0) {
List<LANPeerEvent> lst = null;
Iterator<LANPeerEvent> i = serverLANEventBuffer.iterator();
while(i.hasNext()) {
LANPeerEvent evt = i.next();
if(evt.getPeerId().equals(clientId)) {
i.remove();
if(lst == null) {
lst = new ArrayList<>();
}
lst.add(evt);
}
}
return lst;
}else {
return null;
}
}
private static final int fragmentSize = 65536; private static final int fragmentSize = 65536;

View File

@ -6,10 +6,6 @@ import org.teavm.jso.typedarrays.ArrayBuffer;
public interface EaglercraftLANServer extends JSObject { public interface EaglercraftLANServer extends JSObject {
final int PEERSTATE_FAILED = 0;
final int PEERSTATE_SUCCESS = 1;
final int PEERSTATE_LOADING = 2;
boolean LANServerSupported(); boolean LANServerSupported();
void initializeServer(); void initializeServer();
@ -28,18 +24,6 @@ public interface EaglercraftLANServer extends JSObject {
void sendPacketToRemoteClient(String peerId, ArrayBuffer buffer); void sendPacketToRemoteClient(String peerId, ArrayBuffer buffer);
void resetPeerStates();
int getPeerState();
int getPeerStateConnect();
int getPeerStateInitial();
int getPeerStateDesc();
int getPeerStateIce();
void signalRemoteConnect(String peerId); void signalRemoteConnect(String peerId);
void signalRemoteDescription(String peerId, String descJSON); void signalRemoteDescription(String peerId, String descJSON);

View File

@ -10,10 +10,6 @@ public interface EaglercraftVoiceClient extends JSObject {
int READYSTATE_ABORTED = -1; int READYSTATE_ABORTED = -1;
int READYSTATE_DEVICE_INITIALIZED = 1; int READYSTATE_DEVICE_INITIALIZED = 1;
int PEERSTATE_FAILED = 0;
int PEERSTATE_SUCCESS = 1;
int PEERSTATE_LOADING = 2;
boolean voiceClientSupported(); boolean voiceClientSupported();
void initializeDevices(); void initializeDevices();
@ -34,18 +30,6 @@ public interface EaglercraftVoiceClient extends JSObject {
void mutePeer(String peerId, boolean muted); void mutePeer(String peerId, boolean muted);
void resetPeerStates();
int getPeerState();
int getPeerStateConnect();
int getPeerStateInitial();
int getPeerStateDesc();
int getPeerStateIce();
int getReadyState(); int getReadyState();
int signalConnect(String peerId, boolean offer); int signalConnect(String peerId, boolean offer);