Files
EldenRingDeathCounter/server.ts
s0wlz (Matthias Puchstein) 45f94b1bdf init: Elden Ring death counter with multi-client server
Deno HTTP + WebSocket server serving three pages:
- / desktop with YOU DIED overlay and keyboard controls
- /mobile touch-optimized control page
- /obs transparent browser source for OBS

Count persisted to counter.json, synced in real time across all
connected clients. Compiles to a self-contained Windows .exe via
deno compile.
2026-03-18 02:37:01 +01:00

85 lines
2.4 KiB
TypeScript

import { join } from "jsr:@std/path";
const dir = import.meta.dirname!;
let deaths = 0;
const clients = new Set<WebSocket>();
// Load persisted count
try {
const data = JSON.parse(await Deno.readTextFile("counter.json"));
deaths = Number(data.count) || 0;
} catch {
// No file yet — start at 0
}
function broadcast(payload: string) {
for (const ws of clients) {
if (ws.readyState === WebSocket.OPEN) ws.send(payload);
}
}
async function persist() {
await Deno.writeTextFile("counter.json", JSON.stringify({ count: deaths }));
}
function handleSocket(ws: WebSocket) {
ws.onopen = () => {
clients.add(ws);
ws.send(JSON.stringify({ count: deaths }));
};
ws.onmessage = async (e) => {
try {
const msg = JSON.parse(e.data);
const before = deaths;
if (msg.action === "increment") deaths = Math.min(3999, deaths + 1);
else if (msg.action === "decrement") deaths = Math.max(0, deaths - 1);
else if (msg.action === "reset") deaths = 0;
else return;
if (deaths !== before) await persist();
broadcast(JSON.stringify({ count: deaths }));
} catch {
// Ignore malformed messages
}
};
ws.onclose = () => clients.delete(ws);
ws.onerror = () => clients.delete(ws);
}
async function handler(req: Request): Promise<Response> {
const url = new URL(req.url);
if (url.pathname === "/ws") {
const upgrade = req.headers.get("upgrade") ?? "";
if (upgrade.toLowerCase() !== "websocket") {
return new Response("WebSocket upgrade required", { status: 426 });
}
const { socket, response } = Deno.upgradeWebSocket(req);
handleSocket(socket);
return response;
}
if (url.pathname === "/mobile") {
const html = await Deno.readTextFile(join(dir, "mobile.html"));
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } });
}
if (url.pathname === "/obs") {
const html = await Deno.readTextFile(join(dir, "obs.html"));
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } });
}
// Default: desktop
const html = await Deno.readTextFile(join(dir, "desktop.html"));
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } });
}
console.log("Elden Ring Counter running on http://localhost:8080");
console.log("Mobile: http://<your-local-ip>:8080/mobile");
console.log("Obs: http://<your-local-ip>:8080/obs");
Deno.serve({ port: 8080 }, handler);