From fcc2bdd5f07b10bd7a5722150598a8a007152192 Mon Sep 17 00:00:00 2001 From: sepia Date: Tue, 29 Jul 2025 20:06:50 -0500 Subject: [PATCH] Fix really weird bug where clients just don't visit / when they visit /, somehow. I worked around it by having new WS connections to non-existent games just create the game. --- src/.fuse_hidden0000421900000085 | 114 +++++++++++++++++++++++++++++++ src/index.ts | 11 ++- src/web-socket-handler.tsx | 7 +- 3 files changed, 123 insertions(+), 9 deletions(-) create mode 100755 src/.fuse_hidden0000421900000085 diff --git a/src/.fuse_hidden0000421900000085 b/src/.fuse_hidden0000421900000085 new file mode 100755 index 0000000..787e214 --- /dev/null +++ b/src/.fuse_hidden0000421900000085 @@ -0,0 +1,114 @@ +import { Elysia, t } from 'elysia'; +import { html } from '@elysiajs/html'; +import { staticPlugin } from '@elysiajs/static'; +import { cookie } from '@elysiajs/cookie'; +import { WebSocketHandler } from './web-socket-handler'; +import { ElysiaWS } from 'elysia/dist/ws'; +import { createStoneSvg } from './view/board-renderer'; + +const wsHandler = new WebSocketHandler(); +export type WS = ElysiaWS<{ query: { playerId: string; gameId: string } }>; + +const app = new Elysia() + .use( + staticPlugin({ + assets: './public', + prefix: '/', + }), + ) + .use(cookie()) + .use(html()) + .ws('/ws', { + query: t.Object({ + gameId: t.String(), + playerId: t.String(), + }), + open(ws) { + wsHandler.handleConnection(ws); + }, + message(ws, message) { + wsHandler.handleMessage(ws, message); + }, + close(ws) { + wsHandler.handleDisconnect(ws); + }, + }) + .get('/', async ({ query, cookie, request: _request }) => { + let playerId: string; + const existingPlayerId = cookie.playerId?.value; + if (existingPlayerId) { + playerId = existingPlayerId; + console.log(`Using existing playerId from cookie: ${playerId}`); + } else { + playerId = `player-${Math.random().toString(36).substring(2, 9)}`; + cookie.playerId.set({ + value: playerId, + httpOnly: true, + path: '/', + maxAge: 30 * 24 * 60 * 60, + }); + console.log(`Generated new playerId and set cookie: ${playerId}`); + } + + let gameId = query.gameId as string | undefined; + let gameIdInitialized = false; + if (gameId) { + if (!wsHandler.hasGame(gameId)) { + wsHandler.createGame(gameId); + console.log(`Created new game with provided ID: ${gameId}`); + } + } else { + gameId = wsHandler.createGame(); + gameIdInitialized = true; + console.log(`Created new game without specific ID: ${gameId}`); + } + + if (gameIdInitialized) { + return new Response(null, { + status: 302, + headers: { Location: `/?gameId=${gameId}` }, + }); + } + + const displayName = wsHandler.getPlayerName(playerId); + + const htmlTemplate = await Bun.file('./public/index.html').text(); + let finalHtml = htmlTemplate + .replace( + '', + ``, + ) + .replace( + '', + ``, + ) + .replace( + '', + ``, + ); + + return new Response(finalHtml, { + headers: { 'Content-Type': 'text/html' }, + status: 200, + }); + }) + .get('/black-stone.svg', () => { + const stoneSvg = createStoneSvg('black', false); + return new Response(String(stoneSvg), { + headers: { 'Content-Type': 'image/svg+xml' }, + status: 200, + }); + }) + .get('/white-stone.svg', () => { + const stoneSvg = createStoneSvg('white', false); + return new Response(String(stoneSvg), { + headers: { 'Content-Type': 'image/svg+xml' }, + status: 200, + }); + }); + +const port = Number(process.env.PORT || 3000); + +app.listen(port, () => { + console.log(`🦊 Elysia is running at ${app.server?.hostname}:${port}`); +}); diff --git a/src/index.ts b/src/index.ts index 787e214..314da4e 100755 --- a/src/index.ts +++ b/src/index.ts @@ -51,19 +51,18 @@ const app = new Elysia() } let gameId = query.gameId as string | undefined; - let gameIdInitialized = false; if (gameId) { if (!wsHandler.hasGame(gameId)) { wsHandler.createGame(gameId); console.log(`Created new game with provided ID: ${gameId}`); + } else { + console.log(`Player ${playerId} visited existing game ${gameId}`); } } else { gameId = wsHandler.createGame(); - gameIdInitialized = true; - console.log(`Created new game without specific ID: ${gameId}`); - } - - if (gameIdInitialized) { + console.log( + `Player ${playerId} came without a gameId. Redirecting them to new game ${gameId}.`, + ); return new Response(null, { status: 302, headers: { Location: `/?gameId=${gameId}` }, diff --git a/src/web-socket-handler.tsx b/src/web-socket-handler.tsx index 46ff004..72826ea 100755 --- a/src/web-socket-handler.tsx +++ b/src/web-socket-handler.tsx @@ -13,13 +13,14 @@ export class WebSocketHandler { } public handleConnection(ws: WS): void { - const { gameId } = ws.data.query; + const { gameId, playerId } = ws.data.query; + console.log(`Player ${playerId} is opening a connection to game ${gameId}`); if (this.games.has(gameId)) { const game = this.games.get(gameId)!; game.handleConnection(ws); } else { - ws.send('Error: game not found'); - ws.close(); + this.createGame(gameId); + this.games.get(gameId)!.handleConnection(ws); } }