From 85bd49f5b87957644264a393515baa4152fd7561 Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Sun, 16 Jan 2022 02:37:38 +1100 Subject: boilerplate stuff --- util/src/util/Constants.ts | 3 +++ 1 file changed, 3 insertions(+) (limited to 'util/src') diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts index 5fdf5bc0..a1892105 100644 --- a/util/src/util/Constants.ts +++ b/util/src/util/Constants.ts @@ -73,7 +73,10 @@ export const VoiceOPCodes = { HEARTBEAT: 3, SESSION_DESCRIPTION: 4, SPEAKING: 5, + HEARTBEAT_ACK: 6, + RESUME: 7, HELLO: 8, + RESUMED: 9, CLIENT_CONNECT: 12, CLIENT_DISCONNECT: 13, }; -- cgit 1.5.1 From aa8a9eea6b5475d949ee9124a3f47d8166d019fc Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Mon, 7 Mar 2022 19:15:33 +1100 Subject: augh --- bundle/package.json | 2 +- util/src/util/Constants.ts | 5 +-- webrtc/src/Server.ts | 12 +++++-- webrtc/src/opcodes/Heartbeat.ts | 4 +-- webrtc/src/opcodes/Identify.ts | 37 ++++++++++++++++---- webrtc/src/opcodes/SelectProtocol.ts | 68 ++++++++++++++++++++++++++++-------- webrtc/src/opcodes/Version.ts | 14 ++++++++ webrtc/src/opcodes/index.ts | 3 ++ webrtc/src/start.ts | 6 ++-- webrtc/src/util/Heartbeat.ts | 21 ++++++----- 10 files changed, 133 insertions(+), 39 deletions(-) create mode 100644 webrtc/src/opcodes/Version.ts (limited to 'util/src') diff --git a/bundle/package.json b/bundle/package.json index 0b00b325..aedd963b 100644 --- a/bundle/package.json +++ b/bundle/package.json @@ -112,4 +112,4 @@ "typescript-json-schema": "^0.50.1", "ws": "^7.4.2" } -} +} \ No newline at end of file diff --git a/util/src/util/Constants.ts b/util/src/util/Constants.ts index d5315767..42a2c274 100644 --- a/util/src/util/Constants.ts +++ b/util/src/util/Constants.ts @@ -77,8 +77,9 @@ export const VoiceOPCodes = { RESUME: 7, HELLO: 8, RESUMED: 9, - CLIENT_CONNECT: 12, - CLIENT_DISCONNECT: 13, + CLIENT_CONNECT: 12, // incorrect, op 12 is probably used for video + CLIENT_DISCONNECT: 13, // incorrect + VERSION: 16, //not documented }; export const Events = { diff --git a/webrtc/src/Server.ts b/webrtc/src/Server.ts index 0145a221..1d18d6d1 100644 --- a/webrtc/src/Server.ts +++ b/webrtc/src/Server.ts @@ -6,6 +6,8 @@ import { setHeartbeat } from "./util"; import * as mediasoup from "mediasoup"; import { types as MediasoupTypes } from "mediasoup"; +import Net from "net"; + var port = Number(process.env.PORT); if (isNaN(port)) port = 3004; @@ -13,7 +15,7 @@ export class Server { public ws: WebSocketServer; public mediasoupWorkers: MediasoupTypes.Worker[] = []; public mediasoupRouters: MediasoupTypes.Router[] = []; - public mediasoupTransports: MediasoupTypes.Transport[] = []; + public mediasoupTransports: MediasoupTypes.WebRtcTransport[] = []; constructor() { this.ws = new WebSocketServer({ @@ -26,7 +28,7 @@ export class Server { socket.on("message", async (message: string) => { const payload: Payload = JSON.parse(message); - console.log(payload); + // console.log(payload); if (OPCodeHandlers[payload.op]) try { @@ -68,9 +70,13 @@ export class Server { this.mediasoupRouters.push(router); - router.observer.on("newtransport", async (transport: MediasoupTypes.Transport) => { + router.observer.on("newtransport", async (transport: MediasoupTypes.WebRtcTransport) => { console.log("new transport created [id:%s]", transport.id); + transport.observer.on("sctpstatechange", (state) => { + console.log(state) + }); + await transport.enableTraceEvent(); transport.observer.on("newproducer", (producer: MediasoupTypes.Producer) => { diff --git a/webrtc/src/opcodes/Heartbeat.ts b/webrtc/src/opcodes/Heartbeat.ts index 06d6bcb1..47f33f76 100644 --- a/webrtc/src/opcodes/Heartbeat.ts +++ b/webrtc/src/opcodes/Heartbeat.ts @@ -1,8 +1,8 @@ import { WebSocket } from "@fosscord/gateway"; import { Payload } from "./index"; -import { setHeartbeat } from "./../util"; +import { setHeartbeat } from "../util"; import { Server } from "../Server" export async function onHeartbeat(this: Server, socket: WebSocket, data: Payload) { - await setHeartbeat(socket); + await setHeartbeat(socket, data.d); } \ No newline at end of file diff --git a/webrtc/src/opcodes/Identify.ts b/webrtc/src/opcodes/Identify.ts index e965e3de..d7da5c7c 100644 --- a/webrtc/src/opcodes/Identify.ts +++ b/webrtc/src/opcodes/Identify.ts @@ -28,12 +28,12 @@ export async function onIdentify(this: Server, socket: WebSocket, data: Identify } ); const user = session.user; - const guild = await Guild.findOneOrFail({ id: data.d.server_id }); + const guild = await Guild.findOneOrFail({ id: data.d.server_id }, { relations: ["members"] }); if (!guild.members.find(x => x.id === user.id)) return socket.close(CLOSECODES.Invalid_intent); - var transport = await this.mediasoupRouters[0].createWebRtcTransport({ + var transport = this.mediasoupTransports[0] || await this.mediasoupRouters[0].createWebRtcTransport({ listenIps: [{ ip: "0.0.0.0", announcedIp: "127.0.0.1" }], enableUdp: true, enableTcp: true, @@ -66,13 +66,39 @@ export async function onIdentify(this: Server, socket: WebSocket, data: Identify } */ + + + /* + { + "streams": [ + { "type": "video", "ssrc": 129861, "rtx_ssrc": 129862, "rid": "100", "quality": 100, "active": false } + ], + "ssrc": 129860, + "port": 50003, + "modes": [ + "aead_aes256_gcm_rtpsize", + "aead_aes256_gcm", + "xsalsa20_poly1305_lite_rtpsize", + "xsalsa20_poly1305_lite", + "xsalsa20_poly1305_suffix", + "xsalsa20_poly1305" + ], + "ip": "109.200.213.251", + "experiments": [ + "bwe_conservative_link_estimate", + "bwe_remote_locus_client", + "fixed_keyframe_interval" + ]; + }; + */ + socket.send(JSON.stringify({ op: VoiceOPCodes.READY, d: { - streams: [...data.d.streams.map(x => ({ ...x, rtx_ssrc: 1311886, ssrc: 1311885, active: false, }))], - ssrc: 1, + streams: [...data.d.streams.map(x => ({ ...x, rtx_ssrc: Math.floor(Math.random() * 10000), ssrc: Math.floor(Math.random() * 10000), active: false, }))], + ssrc: Math.floor(Math.random() * 10000), ip: transport.iceCandidates[0].ip, - port: transport.iceCandidates[0].port, + port: "50001", modes: [ "aead_aes256_gcm_rtpsize", "aead_aes256_gcm", @@ -81,7 +107,6 @@ export async function onIdentify(this: Server, socket: WebSocket, data: Identify "xsalsa20_poly1305_suffix", "xsalsa20_poly1305" ], - heartbeat_interval: 1, experiments: [], }, })); diff --git a/webrtc/src/opcodes/SelectProtocol.ts b/webrtc/src/opcodes/SelectProtocol.ts index 36527a8b..a957e14f 100644 --- a/webrtc/src/opcodes/SelectProtocol.ts +++ b/webrtc/src/opcodes/SelectProtocol.ts @@ -87,42 +87,82 @@ export async function onSelectProtocol(this: Server, socket: WebSocket, data: Pa })), */ + const videoCodec = this.mediasoupRouters[0].rtpCapabilities.codecs!.find((x: any) => x.kind === "video")?.mimeType + const audioCodec = this.mediasoupRouters[0].rtpCapabilities.codecs!.find((x: any) => x.kind === "audio") + if (!test_hasMadeProducer) { const producer = await transport.produce({ kind: "audio", rtpParameters: { mid: "audio", codecs: [{ - clockRate: 48000, - payloadType: 111, - mimeType: "audio/opus", - channels: 2, + clockRate: audioCodec!.clockRate, + payloadType: audioCodec!.preferredPayloadType as number, + mimeType: audioCodec!.mimeType, + channels: audioCodec?.channels, }], headerExtensions: res.ext?.map(x => ({ id: x.value, uri: x.uri, - })) + })), }, paused: false, }); - + const consumer = await transport.consume({ producerId: producer.id, - paused: false, + paused: true, rtpCapabilities, - }) - + }); + test_hasMadeProducer = true; } + /* server sends sdp: + + m=audio 50021 ICE/SDP //same port as sent in READY + a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87 + c=IN IP4 109.200.213.132 //same IP as sent in READY + a=rtcp:50021 //same port? + a=ice-ufrag:rTmX + a=ice-pwd:M+ncqWK6SEdHhirOjG2VFA + a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87 + a=candidate:1 1 UDP 4261412862 109.200.213.132 50021 typ host //same IP and PORT + + */ + + + var test = { + "video_codec": "H264", + "sdp": ` + m=audio 50011 ICE/SDP\n + a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87\n + c=IN IP4 109.200.214.156\n + a=rtcp:50011\n + a=ice-ufrag:d0aZ\n + a=ice-pwd:51ubWYu7GSkQRqlH/apTSZ\n + a=fingerprint:sha-256 4A:79:94:16:44:3F:BD:05:41:5A:C7:20:F3:12:54:70:00:73:5D:33:00:2D:2C:80:9B:39:E1:9F:2D:A7:49:87\n + a=candidate:1 1 UDP 4261412862 109.200.214.156 50011 typ host\n`, + "media_session_id": "9e18c981687f2de5399edd5cb3f3babf", + "audio_codec": "opus" + }; + + socket.send(JSON.stringify({ op: VoiceOPCodes.SESSION_DESCRIPTION, d: { - video_codec: data.d.codecs.find((x: any) => x.type === "video").name, - secret_key: new Array(32).fill(null).map(x => Math.random() * 256), - mode: "xsalsa20_poly1305", - media_session_id: this.mediasoupTransports[0].id, - audio_codec: data.d.codecs.find((x: any) => x.type === "audio").name, + video_codec: videoCodec?.substring(6) || undefined, + // mode: "xsalsa20_poly1305", + media_session_id: transport.id, + audio_codec: audioCodec?.mimeType.substring(6), + sdp: `m=audio ${transport.iceCandidates[0].port} ICE/SDP\n` + + `a=fingerprint:sha-256 ${transport.dtlsParameters.fingerprints.find(x => x.algorithm === "sha-256")?.value}\n` + + `c=IN IPV4 ${transport.iceCandidates[0].ip}\n` + + `a=rtcp:${transport.iceCandidates[0].port}\n` + + `a=ice-ufrag:${transport.iceParameters.usernameFragment}\n` + + `a=ice-pwd:${transport.iceParameters.password}\n` + + `a=fingerprint:sha-1 ${transport.dtlsParameters.fingerprints[0].value}\n` + + `a=candidate:1 1 ${transport.iceCandidates[0].protocol} ${transport.iceCandidates[0].priority} ${transport.iceCandidates[0].ip} ${transport.iceCandidates[0].port} typ ${transport.iceCandidates[0].type}` } })); } \ No newline at end of file diff --git a/webrtc/src/opcodes/Version.ts b/webrtc/src/opcodes/Version.ts new file mode 100644 index 00000000..0ea6eb4d --- /dev/null +++ b/webrtc/src/opcodes/Version.ts @@ -0,0 +1,14 @@ +import { WebSocket } from "@fosscord/gateway"; +import { Payload } from "./index"; +import { setHeartbeat } from "../util"; +import { Server } from "../Server" + +export async function onVersion(this: Server, socket: WebSocket, data: Payload) { + socket.send(JSON.stringify({ + op: 16, + d: { + voice: "0.8.31", //version numbers? + rtc_worker: "0.3.18", + } + })) +} \ No newline at end of file diff --git a/webrtc/src/opcodes/index.ts b/webrtc/src/opcodes/index.ts index 9b1eb270..d0f40bc2 100644 --- a/webrtc/src/opcodes/index.ts +++ b/webrtc/src/opcodes/index.ts @@ -15,6 +15,8 @@ import { onSpeaking } from "./Speaking"; import { onResume } from "./Resume"; import { onConnect } from "./Connect"; +import { onVersion } from "./Version"; + export type OPCodeHandler = (this: WebSocket, data: Payload) => any; export default { @@ -34,4 +36,5 @@ export default { //op 13? //op 15? //op 16? empty data on client send but server sends {"voice":"0.8.24+bugfix.voice.streams.opt.branch-ffcefaff7","rtc_worker":"0.3.14-crypto-collision-copy"} + [VoiceOPCodes.VERSION]: onVersion, }; \ No newline at end of file diff --git a/webrtc/src/start.ts b/webrtc/src/start.ts index 299bfce8..98f06ad5 100644 --- a/webrtc/src/start.ts +++ b/webrtc/src/start.ts @@ -1,10 +1,10 @@ +//testing +process.env.DATABASE = "../bundle/database.db"; + import { config } from "dotenv"; config(); import { Server } from "./Server"; -//testing -process.env.DATABASE = "../bundle/database.db"; - const server = new Server(); server.listen(); \ No newline at end of file diff --git a/webrtc/src/util/Heartbeat.ts b/webrtc/src/util/Heartbeat.ts index 7b5ed9cd..8c5e3a7a 100644 --- a/webrtc/src/util/Heartbeat.ts +++ b/webrtc/src/util/Heartbeat.ts @@ -1,18 +1,23 @@ import { WebSocket, CLOSECODES } from "@fosscord/gateway"; import { VoiceOPCodes } from "@fosscord/util"; -export async function setHeartbeat(socket: WebSocket) { +export async function setHeartbeat(socket: WebSocket, nonce?: Number) { if (socket.heartbeatTimeout) clearTimeout(socket.heartbeatTimeout); socket.heartbeatTimeout = setTimeout(() => { return socket.close(CLOSECODES.Session_timed_out); }, 1000 * 45); - socket.send(JSON.stringify({ - op: VoiceOPCodes.HEARTBEAT_ACK, - d: { - v: 6, - heartbeat_interval: 13750, - } - })); + if (!nonce) { + socket.send(JSON.stringify({ + op: VoiceOPCodes.HELLO, + d: { + v: 5, + heartbeat_interval: 13750, + } + })); + } + else { + socket.send(JSON.stringify({ op: VoiceOPCodes.HEARTBEAT_ACK, d: nonce })); + } } \ No newline at end of file -- cgit 1.5.1 From d200d83066bcd49de549742ce0987120a6a5d27d Mon Sep 17 00:00:00 2001 From: Madeline <46743919+MaddyUnderStars@users.noreply.github.com> Date: Tue, 8 Mar 2022 21:22:02 +1100 Subject: Changing Member.premium_since back from Date to number fixes an error in the Discord electron client related to rendering premium status. Client throws "Invalid time value", so I'm guessing it's something to do with premium_since not being the date format they want ( allegedly ISO8601, but works with a plain number, so wtf ) --- util/src/entities/Member.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'util/src') diff --git a/util/src/entities/Member.ts b/util/src/entities/Member.ts index 3c5f9db0..b7406881 100644 --- a/util/src/entities/Member.ts +++ b/util/src/entities/Member.ts @@ -86,7 +86,7 @@ export class Member extends BaseClassWithoutId { joined_at: Date; @Column({ nullable: true }) - premium_since?: Date; + premium_since?: number; @Column() deaf: boolean; -- cgit 1.5.1