From 964053bcb10fc7cc47dd9a7ded5994ec51b27f01 Mon Sep 17 00:00:00 2001 From: Rory& Date: Tue, 18 Aug 2026 19:51:47 +0200 Subject: [PATCH 1/2] Voice opcodes --- src/webrtc/util/Constants.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/webrtc/util/Constants.ts b/src/webrtc/util/Constants.ts index 72aec06a8..13fc981c7 100644 --- a/src/webrtc/util/Constants.ts +++ b/src/webrtc/util/Constants.ts @@ -29,12 +29,30 @@ export enum VoiceOPCodes { RESUME = 7, HELLO = 8, RESUMED = 9, + // 10 is unknown + CLIENTS_CONNECT = 11, VIDEO = 12, CLIENT_DISCONNECT = 13, SESSION_UPDATE = 14, MEDIA_SINK_WANTS = 15, VOICE_BACKEND_VERSION = 16, CHANNEL_OPTIONS_UPDATE = 17, + CLIENT_FLAGS = 18, + CLIENT_PLATFORM = 20, + // crypto crap... + DAVE_PROTOCOL_PREPARE_TRANSITION = 21, + DAVE_PROTOCOL_EXECUTE_TRANSITION = 22, + DAVE_PROTOCOL_TRANSITION_READY = 23, + DAVE_PROTOCOL_PREPARE_EPOCH = 24, + MLS_EXTERNAL_SENDER_PACKAGE = 25, + MLS_KEY_PACKAGE = 26, + MLS_PROPOSALS = 27, + MLS_COMMIT_WELCOME = 28, + MLS_ANNOUNCE_COMMIT_TRANSITION = 29, + MLS_WELCOME = 30, + MLS_INVALID_COMMIT_WELCOME = 31, + // no more cypto crap? + NO_ROUTE = 32, } export type VoicePayload = Omit & { op: VoiceOPCodes }; From e1153f43f51d40aad7adaf9608a7aa08c8753bf8 Mon Sep 17 00:00:00 2001 From: Rory& Date: Tue, 18 Aug 2026 23:23:13 +0200 Subject: [PATCH 2/2] Voice connections --- assets/openapi.json | Bin 1058806 -> 1059690 bytes src/schemas/webrtc/VoiceIdentifySchema.ts | 5 ++-- src/webrtc/events/Close.ts | 17 +++++++++++- src/webrtc/events/Connection.ts | 2 +- src/webrtc/opcodes/SelectProtocol.ts | 30 +++++++++++++++++++++- 5 files changed, 48 insertions(+), 6 deletions(-) diff --git a/assets/openapi.json b/assets/openapi.json index 7c0e3a426e858436216bcc4e1e5456055b19821b..e90b59ff926f398fb8756554cf8390059e8fb1bb 100644 GIT binary patch delta 96 zcmex%-r?0XhlUo$7N!>F7M2#)7Pc1l7LFFq7OpMaCm7jsQ%j06lcx*rF7M2#)7Pc1l7LFFq7OpMaCm7ppSh<0i2Z(urm=B2gx7)A^ H=&1n!Ou`WI diff --git a/src/schemas/webrtc/VoiceIdentifySchema.ts b/src/schemas/webrtc/VoiceIdentifySchema.ts index 203020b86..f437fbdb3 100644 --- a/src/schemas/webrtc/VoiceIdentifySchema.ts +++ b/src/schemas/webrtc/VoiceIdentifySchema.ts @@ -28,7 +28,6 @@ export interface VoiceIdentifySchema { rid: string; quality: number; }[]; - // Discord keeps changing the property name of this, probably will keep changing until Dave is finalized - max_secure_frames_version?: number; - max_dave_protocol_version?: number; + max_secure_frames_version?: number; // pre-release Dave max version field + max_dave_protocol_version?: number; // final Dave max version field } diff --git a/src/webrtc/events/Close.ts b/src/webrtc/events/Close.ts index e88b29380..394051fa0 100644 --- a/src/webrtc/events/Close.ts +++ b/src/webrtc/events/Close.ts @@ -17,9 +17,24 @@ */ import { WebSocket } from "@spacebar/gateway"; +import { mediaServer, Send, VoiceOPCodes, WebRtcWebSocket } from "@spacebar/webrtc"; -export async function onClose(this: WebSocket, code: number, reason: string) { +export async function onClose(this: WebRtcWebSocket, code: number, reason: string) { console.log("[WebRTC] closed", code, reason.toString()); + if (this.user_id && this.webRtcClient) { + const { voiceRoomId } = this.webRtcClient; + const connectedClients = mediaServer.getClientsForRtcServer(voiceRoomId); + + for (const client of connectedClients) { + await Send(client.websocket, { + op: VoiceOPCodes.CLIENTS_CONNECT, + d: { + user_id: this.user_id, + }, + }); + } + } + this.removeAllListeners(); } diff --git a/src/webrtc/events/Connection.ts b/src/webrtc/events/Connection.ts index f18083999..c882f5c47 100644 --- a/src/webrtc/events/Connection.ts +++ b/src/webrtc/events/Connection.ts @@ -52,7 +52,7 @@ export async function Connection(this: WS.Server, socket: WebRtcWebSocket, reque const { searchParams } = new URL(`http://localhost${request.url}`); socket.encoding = "json"; - socket.version = Number(searchParams.get("v")) || 5; + socket.version = Number(searchParams.get("v")) || 9; if (socket.version < 3) return socket.close(CLOSECODES.Unknown_error, "invalid version"); setHeartbeat(socket); diff --git a/src/webrtc/opcodes/SelectProtocol.ts b/src/webrtc/opcodes/SelectProtocol.ts index 6a7339322..b07257507 100644 --- a/src/webrtc/opcodes/SelectProtocol.ts +++ b/src/webrtc/opcodes/SelectProtocol.ts @@ -16,7 +16,7 @@ along with this program. If not, see . */ import { SelectProtocolSchema, validateSchema } from "@spacebar/schemas"; -import { VoiceOPCodes, VoicePayload, WebRtcWebSocket, mediaServer, Send } from "@spacebar/webrtc"; +import { mediaServer, Send, VoiceOPCodes, VoicePayload, WebRtcWebSocket } from "@spacebar/webrtc"; export async function onSelectProtocol(this: WebRtcWebSocket, payload: VoicePayload) { if (!this.webRtcClient) return; @@ -37,4 +37,32 @@ export async function onSelectProtocol(this: WebRtcWebSocket, payload: VoicePayl audio_codec: "opus", }, }); + + const { voiceRoomId } = this.webRtcClient; + const connectedClients = mediaServer.getClientsForRtcServer(voiceRoomId); + const clientConnectData = { + user_ids: connectedClients.values().map((c) => c.user_id), + }; + + for (const client of connectedClients) { + await Send(client.websocket, { + op: VoiceOPCodes.CLIENTS_CONNECT, + d: clientConnectData, + }); + await Send(this, { + op: VoiceOPCodes.CLIENT_FLAGS, + d: { + user_id: client.user_id, + flags: 0, // TODO: clips flags, we currently just send "none" + }, + }); + + await Send(this, { + op: VoiceOPCodes.CLIENT_PLATFORM, + d: { + user_id: client.user_id, + platform: 0, // TODO: send platform, currently we always say desktop... + }, + }); + } }