88 lines
2.0 KiB
TypeScript
88 lines
2.0 KiB
TypeScript
/// <reference lib="deno.ns" />
|
|
|
|
export function startServer(
|
|
hostname: string = "localhost",
|
|
port: number = 8042,
|
|
): () => Promise<void> {
|
|
let nextId = 1;
|
|
|
|
const clientsMap = new Map<number, WebSocket>();
|
|
const clientRoomMap = new Map<number, string>();
|
|
const roomsMap = new Map<string, number[]>();
|
|
|
|
const broadcast = (room: string, message: string): void => {
|
|
const members = roomsMap.get(room)!;
|
|
|
|
for (const member of members) {
|
|
const socket = clientsMap.get(member);
|
|
if (socket && socket.readyState === WebSocket.OPEN) {
|
|
socket.send(message);
|
|
}
|
|
}
|
|
};
|
|
|
|
const commands = {
|
|
join: (id: number, ...args: string[]) => {
|
|
const room = args[0];
|
|
roomsMap.getOrInsert(room, [id]);
|
|
clientRoomMap.getOrInsert(id, room);
|
|
broadcast(room, `${id} has joined!`);
|
|
},
|
|
} as const;
|
|
|
|
const handleConnection = (socket: WebSocket): void => {
|
|
const id = nextId++;
|
|
|
|
socket.onopen = () => {
|
|
clientsMap.set(id, socket);
|
|
socket.send(`Welcome ${id}, type /join <room>.`);
|
|
};
|
|
|
|
socket.onclose = () => {
|
|
const room = clientRoomMap.get(id);
|
|
if (room) {
|
|
clientRoomMap.delete(id);
|
|
const members = roomsMap.get(room)!;
|
|
members.splice(members.indexOf(id), 1);
|
|
broadcast(room, `${id} has left!`);
|
|
}
|
|
clientsMap.delete(id);
|
|
};
|
|
|
|
socket.onmessage = (message: MessageEvent) => {
|
|
const text = String(message.data);
|
|
if (text.startsWith("/")) {
|
|
const [command, ...args] = text.substring(1).split(" ");
|
|
try {
|
|
// @ts-ignore
|
|
commands[command](id, args);
|
|
} catch (error) {
|
|
socket.send(JSON.stringify(error));
|
|
}
|
|
}
|
|
};
|
|
|
|
socket.onerror = console.error;
|
|
};
|
|
|
|
const ac = new AbortController();
|
|
const server = Deno.serve(
|
|
{ hostname, port, signal: ac.signal },
|
|
(request) => {
|
|
if (request.headers.get("upgrade") !== "websocket") {
|
|
return new Response("Upgrade Required", { status: 426 });
|
|
}
|
|
|
|
const { socket, response } = Deno.upgradeWebSocket(request);
|
|
handleConnection(socket);
|
|
|
|
return response;
|
|
},
|
|
);
|
|
|
|
return () => {
|
|
ac.abort();
|
|
return server.finished;
|
|
};
|
|
}
|