diff --git a/src/main/mirror-connection.ts b/src/main/mirror-connection.ts index c026923..868b20a 100644 --- a/src/main/mirror-connection.ts +++ b/src/main/mirror-connection.ts @@ -8,6 +8,9 @@ import { encodeFrame, FrameReader, OP_FRAME, OP_HANDSHAKE } from './ipc-protocol export class MirrorConnection { private readonly sock: net.Socket private readonly reader = new FrameReader() + private ready = false + private pending: Buffer[] = [] + private closeAfterPending = false constructor(socketPath: string, handshakePayload: Buffer, onClose: () => void) { this.sock = net.createConnection(socketPath) @@ -16,25 +19,39 @@ export class MirrorConnection { this.sock.write(encodeFrame(OP_HANDSHAKE, handshakePayload)) }) - // Drain responses; the mirror connection only needs to look like a real client. - this.sock.on('data', (chunk) => this.reader.push(chunk)) + // Discord sends a READY dispatch after the handshake; only once that + // arrives will it accept further commands like SET_ACTIVITY. + this.sock.on('data', (chunk) => { + const hadFrames = this.reader.push(chunk).length > 0 + if (hadFrames && !this.ready) { + this.ready = true + for (const frame of this.pending.splice(0)) this.sock.write(frame) + if (this.closeAfterPending) this.sock.end() + } + }) this.sock.on('error', onClose) this.sock.on('close', onClose) } sendActivity(payload: Buffer): void { - if (this.sock.writable) { - this.sock.write(encodeFrame(OP_FRAME, payload)) + const frame = encodeFrame(OP_FRAME, payload) + if (this.ready) { + if (this.sock.writable) this.sock.write(frame) + } else { + this.pending.push(frame) } } - /** Sends a final frame and closes the connection once it has been flushed. */ + /** Sends a final frame (after the handshake) and closes the connection once it has been flushed. */ sendActivityAndClose(payload: Buffer): void { - if (this.sock.writable) { - this.sock.end(encodeFrame(OP_FRAME, payload)) + const frame = encodeFrame(OP_FRAME, payload) + if (this.ready) { + if (this.sock.writable) this.sock.end(frame) + else this.sock.destroy() } else { - this.sock.destroy() + this.pending.push(frame) + this.closeAfterPending = true } } diff --git a/src/main/relay.ts b/src/main/relay.ts index 7182a01..6895bce 100644 --- a/src/main/relay.ts +++ b/src/main/relay.ts @@ -388,9 +388,14 @@ export class RpcRelay extends EventEmitter { let mirror = this.mirrors.get(index) if (!mirror) { - mirror = new MirrorConnection(mirrorPath, handshakePayload, () => - this.mirrors.delete(index) + const newMirror: MirrorConnection = new MirrorConnection( + mirrorPath, + handshakePayload, + () => { + if (this.mirrors.get(index) === newMirror) this.mirrors.delete(index) + } ) + mirror = newMirror this.mirrors.set(index, mirror) if (frame.op === OP_HANDSHAKE) continue // handshake already sent on connect }