avery-game-proto/index.ts

149 lines
3.9 KiB
TypeScript
Raw Normal View History

2024-10-18 20:55:55 +03:00
import express from "express";
import expressWs from "express-ws";
import type { WebSocket } from "ws";
import crypto from "crypto";
import fs from "fs/promises";
interface Host {
ws: WebSocket,
code: string,
messages: { from: "host"|"join", data: string }[],
join?: Join,
}
interface Join {
ws: WebSocket,
code: string,
host: Host,
}
const hosts: Host[] = [];
const joins: Join[] = [];
const app = expressWs(express()).app;
const port = process.env.PORT || 3000;
app.use(express.json());
app.use(express.static("public"));
app.ws("/", (ws) => {
ws.on("message", (msg: string) => {
console.log(msg);
if (msg === "host") {
if (hosts.some(({ ws: ows }) => ows == ws) || joins.some(({ ws: ows }) => ows == ws)) {
ws.send("failed");
return;
}
const code = crypto.randomBytes(4).toString("hex");
ws.send("code " + code);
hosts.push({ ws, code, messages: [] });
return;
}
if (msg.startsWith("join ")) {
if (hosts.some(({ ws: ows }) => ows == ws) || joins.some(({ ws: ows }) => ows == ws)) {
ws.send("failed");
return;
}
const code = msg.substring(5);
const host = hosts.find(({ code: oldCode }) => oldCode === code);
if (!host || host.join) {
ws.send("failed");
return;
}
ws.send("good");
const join: Join = { ws, code, host };
host.join = join;
joins.push(join);
}
if (msg.startsWith("send ")) {
const send = msg.substring(5);
const host = hosts.find(({ ws: ows }) => ows == ws);
const join = joins.find(({ ws: ows }) => ows == ws);
if (host) {
if (!host.join) {
ws.send("failed");
return;
}
host.join.ws.send("rcv " + translateText(send, host.code));
host.messages.push({ from: "host", data: send });
return;
}
if (join) {
join.host.ws.send("rcv " + translateText(send, join.code));
join.host.messages.push({ from: "join", data: send });
}
}
});
ws.on("close", () => {
let index = hosts.findIndex(({ ws: thisWs }) => thisWs == ws);
if (index > -1) {
const host = hosts[index];
fs.appendFile(
"./out.txt",
`${host.code} | ${new Date().toUTCString()}\n` +
host.messages.map(({ from, data }) => `${from}> ${data}`).join("\n") + "\n\n",
).catch(console.error);
const join = host.join;
hosts.splice(index, 1);
if (join) {
join.ws.send("disconnected");
index = joins.indexOf(join);
if (index > -1) {
joins.splice(index, 1);
}
}
}
index = joins.findIndex(({ ws: thisWs }) => thisWs == ws);
if (index > -1) {
const host = joins[index].host;
joins.splice(index, 1);
host.ws.send("disconnected");
}
});
});
function translateText(msg: string, code: string): string {
const shift = parseInt(code, 16);
return msg
.replaceAll(/[^\x20-\x7e]+/g, "")
.replaceAll(/\s+/g, " ")
.toLowerCase()
.substring(0, 1024)
.split(" ")
.map(w => {
let o = "";
let n = shift;
for (let i = 0; i < w.length; i++) {
const c = w[i];
const cc = c.charCodeAt(0);
n ^= (cc * shift) >> (i % 4);
n = Math.abs(n);
if ("a" <= c && c <= "z") {
o += String.fromCharCode(0x61 + (n % 26));
} else {
const nc = n % (32 + 6 + 4);
if (nc <= 32) {
o += String.fromCharCode(0x21 + nc);
} else if (nc <= 32 + 6) {
o += String.fromCharCode(0x5B + nc - 32);
} else {
o += String.fromCharCode(0x7B + nc - 32 - 6);
}
}
}
return o;
})
.join(" ");
}
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});