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