UNPKG

@tf2pickup-org/mumble-client

Version:
137 lines 4.28 kB
import { Subject } from 'rxjs'; import { packetForType, packetType } from './packet-type-registry.js'; import { UDPTunnel } from '@tf2pickup-org/mumble-protocol'; import { readVarint } from './read-varint.js'; export class MumbleSocket { socket; _packet = new Subject(); _audioPacket = new Subject(); buffers = []; length = 0; readers = []; constructor(socket) { this.socket = socket; this.socket.on('data', (data) => { this.receiveData(data); }); this.readPrefix(); } get packet() { return this._packet.asObservable(); } get audioPacket() { return this._audioPacket.asObservable(); } read(length, callback) { this.readers.push({ length, callback }); if (this.readers.length === 1) { this.flushReaders(); } } async send(message, payload) { const typeNumber = packetType(message); if (typeNumber === undefined) { throw new Error(`unknown message type (${message.typeName})`); } const encoded = message.toBinary(payload); const prefix = Buffer.alloc(6); prefix.writeUint16BE(typeNumber, 0); prefix.writeUint32BE(encoded.length, 2); await this.write(Buffer.concat([prefix, encoded])); } write(buffer) { return new Promise((resolve, reject) => { if (this.socket.writable) { this.socket.write(buffer, err => { if (err) { reject(err); } else { resolve(); } }); } else { reject(new Error('socket not writable')); } }); } end() { this.socket.end(); } receiveData(data) { this.buffers.push(data); this.length += data.length; this.flushReaders(); } flushReaders() { if (this.readers.length === 0) { return; } const reader = this.readers[0]; if (this.length < reader.length) { return; } const buffer = Buffer.alloc(reader.length); let written = 0; while (written < reader.length) { const received = this.buffers[0]; const remaining = reader.length - written; if (received.length <= remaining) { received.copy(buffer, written); written += received.length; this.buffers.splice(0, 1); this.length -= received.length; } else { received.copy(buffer, written, 0, remaining); written += remaining; this.buffers[0] = received.subarray(remaining); this.length -= remaining; } } this.readers.splice(0, 1); reader.callback(buffer); } readPrefix() { this.read(6, prefix => { const type = prefix.readUint16BE(0); const length = prefix.readUint32BE(2); this.readPacket(type, length); }); } readPacket(type, length) { this.read(length, data => { const message = packetForType(type); if (message) { switch (message.typeName) { case UDPTunnel.typeName: this.decodeAudio(data); break; default: this._packet.next({ type, typeName: message.typeName, payload: message.fromBinary(data), }); } } else { console.error(`Unrecognized packet type (${type})`); } this.readPrefix(); }); } decodeAudio(packet) { if (packet.length < 1) { return; } const target = 0b000111111 & packet[0]; if (target !== 0) { return; } const { value } = readVarint(packet.subarray(1)); this._audioPacket.next({ source: value }); } } //# sourceMappingURL=mumble-socket.js.map