This commit is contained in:
sepia 2025-07-19 18:34:53 -05:00
parent 1f19022d45
commit 742bd4f106
8 changed files with 49 additions and 62 deletions

View File

@ -27,9 +27,7 @@
<input type="text" id="game-link" size="50" readonly /> <input type="text" id="game-link" size="50" readonly />
<button onclick="copyGameLink()">Copy</button> <button onclick="copyGameLink()">Copy</button>
</div> </div>
<div id="ws-status" style="margin-top: 10px; color: grey"> <div id="ws-status" style="margin-top: 10px; color: grey">Disconnected</div>
Disconnected
</div>
<script src="scripts/display-ws-connection.js"></script> <script src="scripts/display-ws-connection.js"></script>
<script src="scripts/send-ws-messages.js"></script> <script src="scripts/send-ws-messages.js"></script>
<script src="scripts/copy-game-link.js"></script> <script src="scripts/copy-game-link.js"></script>

View File

@ -22,12 +22,10 @@ htmx.trigger(gameContainer, ' and connect');
// Update the game link input // Update the game link input
const gameLinkInput = document.getElementById('game-link'); const gameLinkInput = document.getElementById('game-link');
if (!gameLinkInput) { if (!gameLinkInput) {
console.error('Missing game-link element.') console.error('Missing game-link element.');
} }
gameLinkInput.value = gameLinkInput.value =
window.location.origin + window.location.origin + window.location.pathname + `?gameId=${gameId}`;
window.location.pathname +
`?gameId=${gameId}`;
// Update the WebSocket status indicator // Update the WebSocket status indicator
const wsStatusDiv = document.getElementById('ws-status'); const wsStatusDiv = document.getElementById('ws-status');

View File

@ -5,9 +5,9 @@ document.addEventListener('htmx:wsConfigSend', function(e) {
// Set the custom JSON data // Set the custom JSON data
e.detail.parameters = { e.detail.parameters = {
type: "make_move", type: 'make_move',
row: row, row: row,
col: col col: col,
}; };
} }
}); });

View File

@ -12,7 +12,7 @@ interface MakeMoveMessage {
col: number; col: number;
} }
type WS = ElysiaWS<{query: {playerId: string, gameId: string}}>; type WS = ElysiaWS<{ query: { playerId: string; gameId: string } }>;
export class WebSocketHandler { export class WebSocketHandler {
private connections: Map<string, Array<WS>>; private connections: Map<string, Array<WS>>;
private games: Map<string, GameInstance>; private games: Map<string, GameInstance>;
@ -54,13 +54,12 @@ export class WebSocketHandler {
private handleMakeMove(ws: WS, message: MakeMoveMessage): void { private handleMakeMove(ws: WS, message: MakeMoveMessage): void {
const { row, col } = message; const { row, col } = message;
const { gameId, playerId } = ws.data.query; const { gameId, playerId } = ws.data.query;
console.log(`Handling make_move message in game ${gameId} from player ${playerId}: ${{message}}`); console.log(
`Handling make_move message in game ${gameId} from player ${playerId}: ${{ message }}`,
);
if (!gameId || !playerId || row === undefined || col === undefined) { if (!gameId || !playerId || row === undefined || col === undefined) {
this.sendMessage( this.sendMessage(ws, 'Error: missing gameId, playerId, row, or col');
ws,
'Error: missing gameId, playerId, row, or col',
);
return; return;
} }
@ -74,10 +73,7 @@ export class WebSocketHandler {
([_, id]) => id === playerId, ([_, id]) => id === playerId,
)?.[0] as ('black' | 'white') | undefined; )?.[0] as ('black' | 'white') | undefined;
if (!playerColor) { if (!playerColor) {
this.sendMessage( this.sendMessage(ws, 'Error: you are not a player in this game');
ws,
'Error: you are not a player in this game',
);
return; return;
} }
@ -106,7 +102,9 @@ export class WebSocketHandler {
const connectionsInGame = this.connections.get(gameId); const connectionsInGame = this.connections.get(gameId);
if (!connectionsInGame) { if (!connectionsInGame) {
console.error(`Disconnecting WebSocket for player ${playerId} from game ${gameId}, but that game has no connections!`); console.error(
`Disconnecting WebSocket for player ${playerId} from game ${gameId}, but that game has no connections!`,
);
return; return;
} }
this.connections.set( this.connections.set(
@ -127,7 +125,9 @@ export class WebSocketHandler {
public broadcastGameState(gameId: string): void { public broadcastGameState(gameId: string): void {
const game = this.games.get(gameId); const game = this.games.get(gameId);
if (!game) { if (!game) {
console.warn('Attempted to broadcast state of game ${gameId}, which is not loaded.'); console.warn(
'Attempted to broadcast state of game ${gameId}, which is not loaded.',
);
return; return;
} }
@ -138,17 +138,17 @@ export class WebSocketHandler {
const updatedBoardHtml = renderGameBoardHtml(game, playerId); const updatedBoardHtml = renderGameBoardHtml(game, playerId);
ws.send(updatedBoardHtml); ws.send(updatedBoardHtml);
const updatedPlayerInfoHtml = renderPlayerInfoHtml( const updatedPlayerInfoHtml = renderPlayerInfoHtml(game.id, playerId);
game.id,
playerId,
);
ws.send(updatedPlayerInfoHtml); ws.send(updatedPlayerInfoHtml);
if (game.status === 'finished') { if (game.status === 'finished') {
if (game.winner === 'draw') { if (game.winner === 'draw') {
this.sendMessageToGame(gameId, 'Game ended in draw.'); this.sendMessageToGame(gameId, 'Game ended in draw.');
} else if (game.winner) { } else if (game.winner) {
this.sendMessageToGame(gameId, `${game.winner.toUpperCase()} wins!`); this.sendMessageToGame(
gameId,
`${game.winner.toUpperCase()} wins!`,
);
} }
} else if (game.status === 'playing') { } else if (game.status === 'playing') {
const clientPlayerColor = Object.entries(game.players).find( const clientPlayerColor = Object.entries(game.players).find(
@ -168,27 +168,21 @@ export class WebSocketHandler {
} }
} }
public sendMessageToGame( public sendMessageToGame(gameId: string, message: string): void {
gameId: string,
message: string,
): void {
const connections = this.connections.get(gameId); const connections = this.connections.get(gameId);
if (connections) { if (connections) {
connections.forEach((ws) => { connections.forEach((ws) => {
ws.send('<div id="messages">' + message + '</div>') ws.send('<div id="messages">' + message + '</div>');
}); });
} }
} }
public sendMessage( public sendMessage(targetWs: WS, message: string): void {
targetWs: WS, targetWs.send('<div id="messages">' + message + '</div>');
message: string,
): void {
targetWs.send('<div id="messages">' + message + '</div>')
} }
public getGame(gameId: string): GameInstance | undefined { public getGame(gameId: string): GameInstance | undefined {
return this.games.get(gameId) return this.games.get(gameId);
} }
createGame(gameId?: string): GameInstance { createGame(gameId?: string): GameInstance {

View File

@ -12,9 +12,8 @@ export function renderGameBoardHtml(
let boardHtml = '<div id="game-board" class="game-board-grid">'; let boardHtml = '<div id="game-board" class="game-board-grid">';
const currentPlayerColor = const currentPlayerColor =
Object.entries(gameState.players).find( Object.entries(gameState.players).find(([_, id]) => id === playerId)?.[0] ||
([_, id]) => id === playerId, null;
)?.[0] || null;
const isPlayersTurn = const isPlayersTurn =
gameState.status === 'playing' && gameState.status === 'playing' &&
gameState.currentPlayer === currentPlayerColor; gameState.currentPlayer === currentPlayerColor;
@ -30,9 +29,7 @@ export function renderGameBoardHtml(
} }
// HTMX attributes for making a move // HTMX attributes for making a move
const wsAttrs = isPlayersTurn && !stone const wsAttrs = isPlayersTurn && !stone ? `ws-send="click"` : '';
? `ws-send="click"`
: '';
boardHtml += ` boardHtml += `
<div <div