gomoku/src/index.ts

114 lines
3.3 KiB
TypeScript
Executable File

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;
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();
console.log(
`Player ${playerId} came without a gameId. Redirecting them to new game ${gameId}.`,
);
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(
'<meta name="gameId" content="" />',
`<meta name="gameId" content="${gameId}" />`,
)
.replace(
'<meta name="playerId" content="" />',
`<meta name="playerId" content="${playerId}" />`,
)
.replace(
'<meta name="displayName" content="" />',
`<meta name="displayName" content="${displayName}" />`,
);
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}`);
});