godot-webrtc-mplayer-testing/server.ts

186 lines
5.2 KiB
TypeScript

const PORT = parseInt(Deno.env.get("PORT") || "80");
const randomInt = (low: number, high: number) =>
Math.floor(Math.random() * (high - low + 1) + low);
const randomSecret = () => new Array(8).map(() => randomInt(0, 10)).join("");
interface Client {
socket: WebSocket;
}
// TODO: version comparison
interface Lobby {
name: string;
clients: Client[];
// TODO: private vs public lobbies?
}
const lobbies = new Set<Lobby>();
const clients = new Set<Client>();
console.log("Listening on port", PORT);
const listener = Deno.listen({ port: PORT });
for await (const conn of listener) {
console.debug("Connection received:", conn);
(async () => {
const server = Deno.serveHttp(conn);
for await (const { respondWith, request } of server) {
console.debug("HTTP Request Received", request);
try {
const { socket, response } = Deno.upgradeWebSocket(request);
const client: Client = { socket };
socket.onmessage = (ev) => {
console.log("Client Message Received", ev);
};
socket.onopen = (ev) => {
console.log("New Client:", ev);
if (!clients.has(client)) clients.add(client);
socket.send("lobbies_start");
lobbies.forEach(({ name, clients }) => {
socket.send(JSON.stringify({ name, numClients: clients.length }));
});
socket.send("lobbies_end");
};
socket.onclose = (ev) => {
console.log("Client Socket Close:", ev);
if (clients.has(client)) clients.delete(client);
};
socket.onerror = (ev) => {
console.log("Client Socket Error:", ev);
if (clients.has(client)) clients.delete(client);
};
respondWith(response);
} catch (e) {
let body = "400 Bad Request";
if (e instanceof TypeError) {
body += " - Expected to be able to upgrade to WebSocket connection";
console.log("Could not add client:", e);
} else {
console.log("Could not add client for unhandled reason:", e);
}
respondWith(
new Response(body, {
status: 400,
headers: { "content-type": "text/html" },
}),
);
}
}
})();
}
/*
function peerCreatesLobby(peer: Peer, lobbyName: string) {
// TODO: ensure we can create a lobby
lobbies.add({
})
}
*/
/*
function peerJoinsLobby(peer: Peer, lobby: Lobby) {
lobbyName = randomSecret();
lobbies.set(lobbyName, new Lobby(lobbyName, this.id));
console.log(`Peer ${this.id} created lobby ${lobbyName}`);
console.log(`Open lobbies: ${lobbies.size}`);
}
const lobby = lobbies.get(lobbyName);
if (!lobby) throw new ProtoError(4000, STR_LOBBY_DOES_NOT_EXIST);
if (lobby.sealed) throw new ProtoError(4000, STR_LOBBY_IS_SEALED);
this.lobby = lobbyName;
console.log(
`Peer ${this.id} joining lobby ${lobbyName} ` +
`with ${lobby.peers.length} peers`,
);
lobby.join(this);
this.ws.send(`J: ${lobbyName}\n`);
}
// TODO: ensure peer not already in a lobby
const assigned = this.getPeerId(peer);
peer.ws.send(`I: ${assigned}\n`);
this.peers.forEach((p) => {
p.ws.send(`N: ${assigned}\n`);
peer.ws.send(`N: ${this.getPeerId(p)}\n`);
});
this.peers.push(peer);
}
*/
/*
function peerLeavesLobby(peer: Peer) {
const idx = this.peers.findIndex((p) => peer === p);
if (idx === -1) return false;
const assigned = this.getPeerId(peer);
const close = assigned === 1;
this.peers.forEach((p) => {
try {
// room host disconnected
if (close) p.ws.close(4000, STR_HOST_DISCONNECTED);
// notify peers
else p.ws.send(`D: ${assigned}\n`);
} catch (e) {
console.error(`Error when leaving: ${e}`);
}
});
this.peers.splice(idx, 1);
if (close && this.closeTimer >= 0) {
// we are closing already.
clearTimeout(this.closeTimer);
this.closeTimer = -1;
}
return close;
}
*/
/*
function graduateLobby(lobby: Lobby) {
// only host can seal
if (peer.id !== this.host) {
throw new ProtoError(4000, STR_ONLY_HOST_CAN_SEAL);
}
this.sealed = true;
this.peers.forEach((p) => {
p.ws.send("S: \n");
});
console.log(
`Peer ${peer.id} sealed lobby ${this.name} ` +
`with ${this.peers.length} peers`,
);
this.closeTimer = setTimeout(() => {
// close peer connection to host (and thus the lobby)
this.peers.forEach((p) => {
p.ws.close(1000, STR_SEAL_COMPLETE);
});
}, SEAL_CLOSE_TIMEOUT);
}
}
*/
/*
function parseMsg(peer: Peer, msg: string) {
// TODO: modify this?
// O: Client is sending an offer.
// A: Client is sending an answer.
// C: Client is sending a candidate.
let destId = parseInt(cmd.substr(3).trim());
// Dest is not an ID.
if (!destId) throw new ProtoError(4000, STR_INVALID_DEST);
if (destId === 1) destId = lobby.host;
const dest = lobby.peers.find((p: Peer) => p.id === destId);
// Dest is not in this room.
if (!dest) throw new ProtoError(4000, STR_INVALID_DEST);
function isCmd(what: string) {
return cmd.startsWith(`${what}: `);
}
if (isCmd("O") || isCmd("A") || isCmd("C")) {
dest.ws.send(cmd[0] + ": " + lobby.getPeerId(peer) + data);
return;
}
throw new ProtoError(4000, STR_INVALID_CMD);
}
*/