initial commit
This commit is contained in:
commit
2597a364bd
|
@ -0,0 +1,3 @@
|
||||||
|
out.txt
|
||||||
|
node_modules/
|
||||||
|
.yarn/
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptLibraryMappings">
|
||||||
|
<includedPredefinedLibrary name="Node.js Core" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,7 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="DiscordProjectSettings">
|
||||||
|
<option name="show" value="ASK" />
|
||||||
|
<option name="description" value="" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/avery-game-proto.iml" filepath="$PROJECT_DIR$/.idea/avery-game-proto.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
|
@ -0,0 +1,2 @@
|
||||||
|
yarnPath: .yarn/releases/yarn-4.4.1.cjs
|
||||||
|
nodeLinker: node-modules
|
|
@ -0,0 +1,12 @@
|
||||||
|
FROM node:22-slim
|
||||||
|
LABEL authors="codedsakura"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN yarn install
|
||||||
|
|
||||||
|
ENV PORT=80
|
||||||
|
ENTRYPOINT ["yarn", "run", "start"]
|
||||||
|
EXPOSE 80
|
|
@ -0,0 +1,7 @@
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build: .
|
||||||
|
container_name: app
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
restart: unless-stopped
|
|
@ -0,0 +1,148 @@
|
||||||
|
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}`);
|
||||||
|
});
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"name": "avery-game-proto",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"start": "ts-node index.ts"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@types/express": "^4.17.21",
|
||||||
|
"@types/express-ws": "^3.0.5",
|
||||||
|
"express": "^5.0.0-beta.3",
|
||||||
|
"express-ws": "^5.0.2",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.5.4"
|
||||||
|
},
|
||||||
|
"packageManager": "yarn@4.4.1"
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Avery game prototype</title>
|
||||||
|
<style>
|
||||||
|
.msg.dir-in::before {
|
||||||
|
content: "< ";
|
||||||
|
}
|
||||||
|
.msg.dir-out::before {
|
||||||
|
content: "> ";
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="setup">
|
||||||
|
<div>
|
||||||
|
<label for="joinCode">Join game:</label>
|
||||||
|
<input id="joinCode" placeholder="Join Code" />
|
||||||
|
<button id="join">Join</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button id="host">Host new game</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="joinCodeNew"></div>
|
||||||
|
<form id="msg" style="display: none">
|
||||||
|
<input placeholder="message" id="msgBox" />
|
||||||
|
<button type="submit">send</button>
|
||||||
|
<hr />
|
||||||
|
<div id="msgs"></div>
|
||||||
|
</form>
|
||||||
|
<footer>
|
||||||
|
<hr />
|
||||||
|
<p>this is a basic prototype, some stuff might be janky</p>
|
||||||
|
<div>
|
||||||
|
rules:
|
||||||
|
<ul>
|
||||||
|
<li>all whitespace is merged into a single space</li>
|
||||||
|
<li>all letters in each word get lower-cased, and (consistently) jumbled around based on the previous character</li>
|
||||||
|
<li>all numbers and symbols get jumbled around, also based on their position</li>
|
||||||
|
<li>other symbols (outside printable ASCII) are voided</li>
|
||||||
|
<li>max message length: 1024 characters (after whitespace and other symbol striping)</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<p>
|
||||||
|
idea by Avery [Kelly], prototype by CodedSakura
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
<script>
|
||||||
|
const setup = document.getElementById("setup");
|
||||||
|
const form = document.getElementById("msg");
|
||||||
|
|
||||||
|
document.getElementById("join").addEventListener("click", () => {
|
||||||
|
const joinCode = document.getElementById("joinCode");
|
||||||
|
ws.send("join " + joinCode.value.toLowerCase());
|
||||||
|
});
|
||||||
|
document.getElementById("host").addEventListener("click", () => {
|
||||||
|
ws.send("host");
|
||||||
|
});
|
||||||
|
|
||||||
|
form.addEventListener("submit", e => {
|
||||||
|
e.preventDefault();
|
||||||
|
const data = document.getElementById("msgBox").value
|
||||||
|
.replaceAll(/[^\x20-\x7e]+/g, "")
|
||||||
|
.replaceAll(/\s+/g, " ")
|
||||||
|
.toLowerCase()
|
||||||
|
.substring(0, 1024);
|
||||||
|
ws.send("send " + data);
|
||||||
|
newMessage(data, "out");
|
||||||
|
document.getElementById("msgBox").value = "";
|
||||||
|
});
|
||||||
|
|
||||||
|
const ws = new WebSocket("ws://" + window.location.host);
|
||||||
|
ws.addEventListener("message", ({ data: msg }) => {
|
||||||
|
console.log(msg);
|
||||||
|
|
||||||
|
if (msg.startsWith("code ")) {
|
||||||
|
document.getElementById("joinCodeNew").innerText = "code: " + msg.substring(5);
|
||||||
|
setup.style.display = "none";
|
||||||
|
form.style.display = "";
|
||||||
|
const msgs = document.getElementById("msgs");
|
||||||
|
while (msgs.lastChild) msgs.removeChild(msgs.lastChild);
|
||||||
|
}
|
||||||
|
if (msg === "good") {
|
||||||
|
setup.style.display = "none";
|
||||||
|
form.style.display = "";
|
||||||
|
const msgs = document.getElementById("msgs");
|
||||||
|
while (msgs.lastChild) msgs.removeChild(msgs.lastChild);
|
||||||
|
}
|
||||||
|
if (msg.startsWith("rcv ")) {
|
||||||
|
const data = msg.substring(4);
|
||||||
|
newMessage(data, "in");
|
||||||
|
}
|
||||||
|
if (msg === "disconnected") {
|
||||||
|
setup.style.display = "";
|
||||||
|
form.style.display = "none";
|
||||||
|
document.getElementById("joinCodeNew").innerText = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function newMessage(data, direction) {
|
||||||
|
const elem = document.createElement("div");
|
||||||
|
elem.classList.add("msg", `dir-${direction}`);
|
||||||
|
elem.appendChild(document.createTextNode(data));
|
||||||
|
elem.title = new Date().toLocaleTimeString();
|
||||||
|
document.getElementById("msgs").prepend(elem);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "CommonJS",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue