114 lines
3.3 KiB
TypeScript
Executable File
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}`);
|
|
});
|