]> git.cworth.org Git - lmno-server/commitdiff
Add tracking of client placement of tiles
authorCarl Worth <cworth@cworth.org>
Fri, 6 Mar 2026 14:36:01 +0000 (09:36 -0500)
committerCarl Worth <cworth@cworth.org>
Fri, 6 Mar 2026 14:36:01 +0000 (09:36 -0500)
This adds a new /place endpoint to be told where tiles are placed, and
allows the server to send all placed tiles when a player
reconnects. This means that a client refresh no longer clears the
entire board, (which was really annoying).

letterrip.js

index 24b7fd1c0dec20dcf98040e19ed391a3a83d318d..abeb9758725e451a26a91ccf1392f5bf40943787 100644 (file)
@@ -32,6 +32,7 @@ class LetterRip extends Game {
     this.state = {
       bag: LetterRip.make_bag(),
       player_tiles: {},
+      player_boards: {},
       stuck: new Set(),
       finished: false,
       winner: null
@@ -202,6 +203,39 @@ class LetterRip extends Game {
     response.json({ finished: false });
   }
 
+  handle_place(request, response) {
+    const session_id = request.session.id;
+
+    if (!this.state.player_tiles[session_id]) {
+      response.sendStatus(400);
+      return;
+    }
+
+    const { tileIndex, r, c } = request.body;
+
+    if (!this.state.player_boards[session_id]) {
+      this.state.player_boards[session_id] = {};
+    }
+
+    const board = this.state.player_boards[session_id];
+
+    /* Remove this tile from any previous position. */
+    for (const key of Object.keys(board)) {
+      if (board[key].tileIndex === tileIndex) {
+        delete board[key];
+        break;
+      }
+    }
+
+    /* Place on the grid (r and c are null when returning to rack). */
+    if (r !== null && c !== null) {
+      board[r + "," + c] = { tileIndex, letter: request.body.letter,
+                             isBlank: request.body.isBlank || false };
+    }
+
+    response.sendStatus(200);
+  }
+
   handle_events(request, response) {
     super.handle_events(request, response);
 
@@ -215,6 +249,12 @@ class LetterRip extends Game {
       response.write(`event: tiles\ndata: ${data}\n\n`);
     }
 
+    /* Send this player's board layout for reconnect recovery. */
+    const board = this.state.player_boards[session_id];
+    if (board && Object.keys(board).length > 0) {
+      response.write(`event: board\ndata: ${JSON.stringify(board)}\n\n`);
+    }
+
     /* Send current bag count. */
     response.write(`event: dealt\ndata: ${JSON.stringify({
       remaining: this.state.bag.length
@@ -250,6 +290,10 @@ router.post('/stuck', (request, response) => {
   request.game.handle_stuck(request, response);
 });
 
+router.post('/place', (request, response) => {
+  request.game.handle_place(request, response);
+});
+
 router.post('/complete', (request, response) => {
   request.game.handle_complete(request, response);
 });