mudb
Version:
Real-time database for multiplayer games
197 lines • 7.21 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const system_1 = require("../../scheduler/system");
const logger_1 = require("../../logger");
const socket_1 = require("../socket");
const rtc_1 = require("./rtc");
const error_1 = require("../../util/error");
const error = error_1.makeError('socket/webrtc/client');
const isBrowser = typeof self !== undefined && !!self && self['Object'] === Object;
function noop() { }
class MuRTCSocket {
constructor(spec) {
this._state = socket_1.MuSocketState.INIT;
this._onClose = noop;
this._pendingCandidates = [];
if (isBrowser && !rtc_1.browserRTC()) {
throw error(`browser doesn't support WebRTC`);
}
if (!isBrowser && !spec.wrtc) {
throw error(`specify WebRTC binding via spec.wrtc`);
}
this.wrtc = rtc_1.browserRTC() || spec.wrtc;
this.sessionId = spec.sessionId;
this._signal = spec.signal;
const pcConfig = spec.pcConfig || {
iceServers: [
{ urls: 'stun:global.stun.twilio.com:3478' },
],
};
pcConfig.sdpSemantics = 'unified-plan';
this._pc = new this.wrtc.RTCPeerConnection(pcConfig);
this._reliableChannel = this._pc.createDataChannel('mudb-reliable', spec.reliableConfig || {});
this._unreliableChannel = this._pc.createDataChannel('mudb-unreliable', spec.unreliableConfig || {
ordered: false,
maxRetransmits: 0,
});
this._reliableChannel.binaryType = this._unreliableChannel.binaryType = 'arraybuffer';
this._pc.onicecandidate = (ev) => {
if (this._state !== socket_1.MuSocketState.INIT) {
return;
}
if (ev.candidate) {
const candidate = ev.candidate.toJSON();
candidate['sid'] = this.sessionId;
this._signal(candidate);
}
};
this._offerOpts = spec.offerOpts || {};
this._scheduler = spec.scheduler || system_1.MuSystemScheduler;
this._logger = spec.logger || logger_1.MuDefaultLogger;
}
state() { return this._state; }
open(spec) {
if (this._state !== socket_1.MuSocketState.INIT) {
throw error(`attempt to connect when connection is ${this._state === socket_1.MuSocketState.OPEN ? 'open' : 'closed'}`);
}
if (isBrowser) {
window.addEventListener('beforeunload', () => {
this.close();
});
}
const maybeReady = () => {
if (this._state !== socket_1.MuSocketState.INIT) {
return;
}
if (this._reliableChannel.readyState === 'open' &&
this._unreliableChannel.readyState === 'open') {
this._state = socket_1.MuSocketState.OPEN;
spec.ready();
}
};
this._reliableChannel.onopen = maybeReady;
this._unreliableChannel.onopen = maybeReady;
this._reliableChannel.onmessage = ({ data }) => {
if (this._state !== socket_1.MuSocketState.OPEN) {
return;
}
if (typeof data !== 'string') {
spec.message(new Uint8Array(data), false);
}
else {
spec.message(data, false);
}
};
this._unreliableChannel.onmessage = ({ data }) => {
if (this._state !== socket_1.MuSocketState.OPEN) {
return;
}
if (typeof data !== 'string') {
spec.message(new Uint8Array(data), true);
}
else {
spec.message(data, true);
}
};
const onChannelClose = (ev) => {
if (this._state === socket_1.MuSocketState.CLOSED) {
return;
}
this._logger.error(`${ev.target.channel} channel closed unexpectedly`);
this.close();
};
this._reliableChannel.onclose = onChannelClose;
this._unreliableChannel.onclose = onChannelClose;
this._reliableChannel.onerror = (e) => this.close(e);
this._unreliableChannel.onerror = (e) => this.close(e);
this._onClose = spec.close;
this._scheduler.setTimeout(() => {
this._pc.createOffer(this._offerOpts)
.then((offer) => {
if (this._state === socket_1.MuSocketState.CLOSED) {
return;
}
this._pc.setLocalDescription(offer)
.then(() => {
if (this._state === socket_1.MuSocketState.CLOSED) {
return;
}
offer['sid'] = this.sessionId;
this._signal(offer);
})
.catch((e) => this.close(e));
})
.catch((e) => this.close(e));
}, 0);
}
handleSignal(data) {
if (this._state !== socket_1.MuSocketState.INIT) {
return;
}
if (!('sdp' in data) && !('candidate' in data)) {
this.close();
throw error(`invalid signal: ${data}`);
}
const pc = this._pc;
if ('sdp' in data) {
pc.setRemoteDescription(data)
.then(() => {
if (this._state === socket_1.MuSocketState.CLOSED) {
return;
}
for (let i = 0; i < this._pendingCandidates.length; ++i) {
pc.addIceCandidate(this._pendingCandidates[i]).catch((e) => this.close(e));
}
this._pendingCandidates.length = 0;
}).catch((e) => {
this.close(e);
});
}
else if ('candidate' in data) {
if (pc.remoteDescription && pc.remoteDescription.type) {
pc.addIceCandidate(data).catch((e) => this.close(e));
}
else {
this._pendingCandidates.push(data);
}
}
}
send(data, unreliable) {
if (this._state !== socket_1.MuSocketState.OPEN) {
return;
}
if (unreliable) {
this._unreliableChannel.send(data);
}
else {
this._reliableChannel.send(data);
}
}
close(e) {
if (this._state === socket_1.MuSocketState.CLOSED) {
return;
}
if (e) {
this._logger.exception(e);
}
this._state = socket_1.MuSocketState.CLOSED;
this._pc.close();
this._reliableChannel.onopen = null;
this._reliableChannel.onmessage = null;
this._unreliableChannel.onopen = null;
this._unreliableChannel.onmessage = null;
this._pc.onicecandidate = null;
this._reliableChannel = null;
this._unreliableChannel = null;
this._pc = null;
this._onClose();
}
reliableBufferedAmount() {
return 0;
}
unreliableBufferedAmount() {
return 0;
}
}
exports.MuRTCSocket = MuRTCSocket;
//# sourceMappingURL=client.js.map