MWSE/sdk/Peer.js

196 lines
5.8 KiB
JavaScript

import MWSEEventTarget from './EventTarget.js';
import { PeerInfo } from './PeerInfo.js';
import WebRTC from './WebRTC.js';
export default class Peer extends MWSEEventTarget {
constructor(mwse) {
super();
this.mwse = mwse;
this.options = {};
this.socketId = undefined;
this.selfSocket = false;
this.active = false;
this.peerConnection = false;
this.primaryChannel = 'datachannel';
this.info = new PeerInfo(this);
this.rtc = this._createRTC();
// Route incoming packs: RTC signaling goes to the RTC engine,
// everything else surfaces as a 'message' event.
this.on('pack', data => {
if (data.type === ':rtcpack:') {
this.rtc.emit('input', data.payload);
} else {
this.emit('message', data);
}
});
}
_createRTC(rtcConfig, rtcServers) {
const rtc = new WebRTC(rtcConfig, rtcServers);
rtc.peer = this;
rtc.on('connected', () => { this.peerConnection = true; });
rtc.on('disconnected', () => { this.peerConnection = false; });
// RTC output → relay opaque `:rtcpack:` to the paired peer via server.
rtc.on('output', payload => {
this.send({ type: ':rtcpack:', payload });
});
// RTC data-channel message → surface as peer 'pack' event.
rtc.on('message', payload => {
this.emit('pack', payload);
});
this.rtc = rtc;
return rtc;
}
// createRTC allows callers to (re)create the RTC object with custom config.
createRTC(rtcConfig, rtcServers) {
return this._createRTC(rtcConfig, rtcServers);
}
setPeerOptions(options) {
if (typeof options === 'string') {
this.setSocketId(options);
} else {
this.options = options;
}
}
setSocketId(uuid) {
this.socketId = uuid;
}
async metadata() {
if (this.socketId === 'me') {
const result = await this.mwse.EventPooling.request({ type: 'my/socketid' });
this.selfSocket = true;
this.active = this.active || true;
this.socketId = result;
this.emit('scope');
this.activeScope = true;
return result;
}
}
async request(pack) {
if (this.active) {
return this.mwse.request(this.socketId, pack);
}
}
equalTo(peer) {
return this.socketId === peer.socketId;
}
async isReachable() {
return this.mwse.EventPooling.request({ type: 'is/reachable', to: this.socketId });
}
async enablePairAuth() {
await this.mwse.EventPooling.request({ type: 'auth/pair-system', value: 'everybody' });
}
async disablePairAuth() {
await this.mwse.EventPooling.request({ type: 'auth/pair-system', value: 'disable' });
}
async enablePairInfo() {
await this.mwse.EventPooling.request({ type: 'connection/pairinfo', value: true });
}
async disablePairInfo() {
await this.mwse.EventPooling.request({ type: 'connection/pairinfo', value: false });
}
async requestPair() {
const { message, status } = await this.mwse.EventPooling.request({
type: 'request/pair',
to: this.socketId
});
if (message === 'ALREADY-PAIRED' || message === 'ALREADY-REQUESTED') {
console.warn('MWSE: already paired or pair requested');
}
if (status === 'fail') {
console.error('MWSE: requestPair failed', status, message);
return false;
}
return true;
}
async endPair() {
await this.mwse.EventPooling.request({ type: 'end/pair', to: this.socketId });
this.forget();
}
async acceptPair() {
const { message, status } = await this.mwse.EventPooling.request({
type: 'accept/pair',
to: this.socketId
});
if (status === 'fail') {
console.error('MWSE: acceptPair failed', status, message);
return false;
}
return true;
}
async rejectPair() {
const { message, status } = await this.mwse.EventPooling.request({
type: 'reject/pair',
to: this.socketId
});
if (status === 'fail') {
console.error('MWSE: rejectPair failed', status, message);
return false;
}
return true;
}
async getPairedList() {
const { value } = await this.mwse.EventPooling.request({
type: 'pair/list',
to: this.socketId
});
return value;
}
async send(pack) {
const p2pOpen = this.peerConnection && this.rtc?.active;
const serverOpen = this.mwse.server.connected;
let channel;
if (p2pOpen && serverOpen) {
channel = this.primaryChannel === 'websocket' ? 'websocket' : 'datachannel';
} else if (serverOpen) {
channel = 'websocket';
} else {
channel = 'datachannel';
}
if (channel === 'websocket') {
if (!this.mwse.writable) {
console.warn('MWSE: socket is not writable');
return;
}
// WOM — no waiter registered; the engine returns nil for pack/to (#33).
this.mwse.EventPooling.only({ type: 'pack/to', pack, to: this.socketId });
} else {
if (pack.type !== ':rtcpack:') {
this.rtc?.sendMessage(pack);
} else {
console.warn('MWSE: cannot send :rtcpack: over data channel');
}
}
}
forget() {
this.mwse.peers.delete(this.socketId);
this.mwse.pairs.delete(this.socketId);
}
}