This commit is contained in:
Spencer Pogorzelski 2023-08-18 13:55:32 -07:00
parent 82a314b54f
commit 85107cbbeb
3 changed files with 87 additions and 96 deletions

View file

@ -5,11 +5,10 @@ import expressWs from "express-ws";
import { AdriftServer, connectTracker } from "./server"; import { AdriftServer, connectTracker } from "./server";
import WebSocket from "isomorphic-ws"; import WebSocket from "isomorphic-ws";
import { answerRtc, bufferToArrayBuffer, connect } from "./rtc"; import { answerRtc, bufferToArrayBuffer } from "./rtc";
dotenv.config(); dotenv.config();
const app = express() as unknown as expressWs.Application; const app = express() as unknown as expressWs.Application;
expressWs(app); expressWs(app);
@ -23,9 +22,6 @@ app.use((_req, res, next) => {
next(); next();
}); });
app.post("/connect", (req, res) => { app.post("/connect", (req, res) => {
const data = req.body; const data = req.body;
answerRtc(data, (d) => { answerRtc(data, (d) => {
@ -51,12 +47,9 @@ app.ws("/dev-ws", (ws, _req) => {
}); });
try { try {
let tracker = new WebSocket("ws://localhost:17776/join"); let tracker = new WebSocket("ws://localhost:17776/join");
tracker.onerror = console.error; tracker.onerror = console.error;
connectTracker(tracker); connectTracker(tracker);
} catch (_) { } catch (_) {}
}
app.listen(3000, () => console.log("listening")); app.listen(3000, () => console.log("listening"));

View file

@ -1,102 +1,101 @@
const configuration = { const configuration = {
iceServers: [ iceServers: [
{ {
urls: "stun:stun.l.google.com:19302", urls: "stun:stun.l.google.com:19302",
}, },
], ],
}; };
import wrtc from "wrtc"; import wrtc from "wrtc";
import { AdriftServer } from "./server"; import { AdriftServer } from "./server";
export async function connect( export async function connect(
offer: RTCSessionDescriptionInit, offer: RTCSessionDescriptionInit,
candidates: RTCIceCandidateInit[], candidates: RTCIceCandidateInit[],
onAnswer: (answer: Record<string, any>) => void onAnswer: (answer: Record<string, any>) => void
): Promise<RTCDataChannel> { ): Promise<RTCDataChannel> {
const localCandidates: any[] = []; const localCandidates: any[] = [];
let dataChannel; let dataChannel;
const peer: RTCPeerConnection = new wrtc.RTCPeerConnection(configuration); const peer: RTCPeerConnection = new wrtc.RTCPeerConnection(configuration);
let promise = new Promise((resolve) => { let promise = new Promise((resolve) => {
peer.ondatachannel = (event) => { peer.ondatachannel = (event) => {
dataChannel = event.channel; dataChannel = event.channel;
resolve(dataChannel); resolve(dataChannel);
};
});
peer.onconnectionstatechange = () => {
console.log("Connection state:", peer.connectionState);
}; };
peer.onsignalingstatechange = () => { });
console.log("Signaling state:", peer.signalingState); peer.onconnectionstatechange = () => {
}; console.log("Connection state:", peer.connectionState);
peer.oniceconnectionstatechange = () => { };
console.log("ICE connection state:", peer.iceConnectionState); peer.onsignalingstatechange = () => {
}; console.log("Signaling state:", peer.signalingState);
peer.onicegatheringstatechange = () => { };
console.log("ICE gathering state:", peer.iceGatheringState); peer.oniceconnectionstatechange = () => {
}; console.log("ICE connection state:", peer.iceConnectionState);
peer.onicecandidate = (event: any) => { };
console.log("onicecandidate"); peer.onicegatheringstatechange = () => {
if (event.candidate) { console.log("ICE gathering state:", peer.iceGatheringState);
localCandidates.push(event.candidate); };
return; peer.onicecandidate = (event: any) => {
} console.log("onicecandidate");
let payload = { if (event.candidate) {
answer: peer.localDescription, localCandidates.push(event.candidate);
candidates: localCandidates, return;
};
onAnswer(payload);
};
await peer.setRemoteDescription(offer);
let answer = await peer.createAnswer();
await peer.setLocalDescription(answer);
for (let candidate of candidates) {
if (!candidate.candidate) continue;
console.log({ candidate });
await peer.addIceCandidate(candidate);
} }
let payload = {
answer: peer.localDescription,
candidates: localCandidates,
};
onAnswer(payload);
};
await peer.setRemoteDescription(offer);
let answer = await peer.createAnswer();
await peer.setLocalDescription(answer);
for (let candidate of candidates) {
if (!candidate.candidate) continue;
console.log({ candidate });
await peer.addIceCandidate(candidate);
}
return promise as any; return promise as any;
} }
export async function answerRtc(data: any, onrespond: (answer: any) => void) { export async function answerRtc(data: any, onrespond: (answer: any) => void) {
if (data && data.offer && data.localCandidates) { if (data && data.offer && data.localCandidates) {
const { offer, localCandidates } = data; const { offer, localCandidates } = data;
let didAnswer = false; let didAnswer = false;
let dataChannel = await connect(offer, localCandidates, (answer) => { let dataChannel = await connect(offer, localCandidates, (answer) => {
if (!didAnswer) { if (!didAnswer) {
didAnswer = true; didAnswer = true;
onrespond(answer); onrespond(answer);
// res.json(answer); // res.json(answer);
} }
}); });
dataChannel.binaryType = "arraybuffer"; dataChannel.binaryType = "arraybuffer";
let server: AdriftServer; let server: AdriftServer;
dataChannel.onopen = () => { dataChannel.onopen = () => {
console.log("opened"); console.log("opened");
server = new AdriftServer((msg) => { server = new AdriftServer((msg) => {
if (dataChannel.readyState === "open") if (dataChannel.readyState === "open") dataChannel.send(msg);
dataChannel.send(msg) });
}); };
}; dataChannel.onclose = () => {
dataChannel.onclose = () => { console.log("closed");
console.log("closed"); server.onClose();
server.onClose(); };
}; dataChannel.onmessage = (event) => {
dataChannel.onmessage = (event) => { if (event.data instanceof ArrayBuffer) {
if (event.data instanceof ArrayBuffer) { server.onMsg(event.data);
server.onMsg(event.data); return;
return; }
} if (event.data instanceof Buffer) {
if (event.data instanceof Buffer) { server.onMsg(bufferToArrayBuffer(event.data));
server.onMsg(bufferToArrayBuffer(event.data)); return;
return; }
} throw new Error("Unexpected datachannel message type");
throw new Error("Unexpected datachannel message type"); };
}; }
}
} }
export function bufferToArrayBuffer(buf: Buffer): ArrayBuffer { export function bufferToArrayBuffer(buf: Buffer): ArrayBuffer {
return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength); return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
} }

View file

@ -16,7 +16,6 @@ import { Readable, Writable } from "stream";
import { BareError, bareInitialFetch, fetchResponse, options } from "./http"; import { BareError, bareInitialFetch, fetchResponse, options } from "./http";
import { answerRtc } from "./rtc"; import { answerRtc } from "./rtc";
function bareErrorToResponse(e: BareError): { function bareErrorToResponse(e: BareError): {
payload: HTTPResponsePayload; payload: HTTPResponsePayload;
body: AsyncIterable<ArrayBuffer>; body: AsyncIterable<ArrayBuffer>;
@ -382,6 +381,6 @@ export function connectTracker(tracker: WebSocket) {
answerRtc(data, (answer) => { answerRtc(data, (answer) => {
console.log("have an answer"); console.log("have an answer");
tracker.send(JSON.stringify(answer)); tracker.send(JSON.stringify(answer));
}) });
}); });
} }