Develop - #1
Merged
Merged
Conversation
added 30 commits
May 22, 2026 19:50
Persist chosen player names across join/reconnect, anchor power-up orbs on ghost cells, split large server/client modules, ship esbuild bundle with index.html swap, and run PM2 as the deploy user (skip certbot dry-run).
Claims trigger when a tetromino lands on or beside an orb cell; spawns only next to board structure with a grid search near home zones; caps and tick rate reduced; placement callback returns claim results for the client.
Clears void orbs left in persisted worlds immediately after deploy instead of waiting for the first spawn tick.
Orbs spawn in empty sky again — bridging to them is the point. Kings now spend a life and respawn at home on row-clear / decay deaths; the third death eliminates the player. A small info card in the corner shows the selected piece's type, moves, distance, and captures.
- Wing fall fade now clones materials per-piece so it no longer zeros opacity on every piece sharing the same cached material. - Clicking the selected piece deselects it; pieces adjacent to a valid move target require a double-click so the green cell stays clickable. - Degraded home cells (`fromHomeZone`) break line-clear runs so a returning idle player isn't wiped by a single placement turning their old 8-cell home row into a clear. - Online-but-idle players degrade after ~1 hr instead of 5 min. - New manual pause / resume with limited uses (4 × 30 min, 60 min total) that freezes degradation, capture, and line-clear for the calling player.
* BoardManager / LineClearService: line clear is now bounded to the qualifying run. Home cells, degraded-home remnants, and paused- player cells already broke the *count*, but the destructive step still stripped the whole row, wiping cells on the far side of any such gap. `_findClearableLines` now returns per-line run ranges, `findClearableLines` exposes `rowRuns` / `colRuns`, and `_clearLine` clips destruction to those ranges. Multiple qualifying runs in the same line both clear independently. Two new regression tests in `tests/server/BoardManager.test.js` lock the behaviour in. * Admin gate: `/admin/advertisers` and the destructive `/api/advertisers` routes (POST/PUT/DELETE/activate, list, stats) now require an `ADMIN_TOKEN` env var when `NODE_ENV=production`. Open in development so local workflows aren't disturbed. * Log noise: the "Player … has N disconnected islands after row clear" line was spamming PM2 (~1 350 of 1 500 captured lines). It's now a single summary that only fires when the count changes. * Dead assets removed from `public/`: `enhanced.html`, `enhanced-game.html`, `interaction-test.html`, `dev-test.html`, and the never-imported `debugHelper.js`. * Pause button: retries `pause_status` once after 2.5 s if the very first request loses the race with the socket join, so it no longer gets stuck on "Pause (loading…)". Adds a tooltip and a cleaner default label. * docs/players-bible.md + 07-changelog-may-2026-c.md updated.
* Pause button shipped as a no-op because `wirePauseButton()` ran
before `playerBar` was appended to the document — getElementById
returned null and the click listener never attached. Bar is now
attached to `document.body` first; rename + pause wiring then
happens against the live DOM tree.
* Advertise link was hidden behind the next-piece HUD (z-index 100
vs HUD's 1000 in the same top-right corner). Moved to bottom-left
above the camera controls strip, z 1100, low-key styling.
* Mobile / touch:
- touchGestures.js: single tap → rotate clockwise, double-tap →
hard drop, swipe → move, long-press is retired (was awkward
overlap with double-tap drop).
- touchControlPad.js (new): on-screen ◀▲▼▶ ⟳ ⬇ pad that auto-
shows on touch-capable devices during the tetris phase.
Wired into inputManager.setupInputHandlers alongside the
existing gesture / keyboard handlers.
* External API for AI players and spectator/fly-through tools:
- POST /api/computer-players/register now seeds a real World
player record (was previously stored only in a process-local
Map, so the issued playerId was effectively never recognised
by the socket layer).
- server/sockets/connection.js reads {playerId, apiToken} from
the handshake query (or cookies) and, when valid, claims the
registered identity. Wrong tokens are rejected outright via
auth_error + disconnect — no silent downgrade.
- examples/random-bot.js demonstrates the full lifecycle:
register → connect with token → join → play valid random moves.
- examples/spectator-feed.js demonstrates the read-only path:
connect → get_activity_log snapshot → stream live events.
- docs/external-api.md is the new single source of truth for the
REST + Socket.IO contract.
- The three obsolete computer-player-*.md docs are stamped with
a deprecation banner pointing at the new doc.
- README links to the new doc and worked examples.
* tests/server/externalAiAuth.test.js (3 tests) locks in the
registration → World-seed → token-validation contract.
All 409 server tests pass.
* `request_tetromino` accepted only `(callback)` — the browser never used it but external bots do. Now accepts either calling convention. * `random-bot.js` is a real demo: requests fresh pieces, tries 40 anchor+offset+rotation combos per piece (mirrors in-process AI), drives itself on a 2.5s placement + 1.8s chess interval. Verified end-to-end against production: places valid tetrominoes and executes valid chess moves. * Throttle `chess_move_rejected` activity log entries to one per player per 1.5s. Without this an over-eager bot (or a human spam-clicking) could fill the rolling 200-event buffer with their own bad attempts in seconds, smothering interesting events for spectators / fly-throughs. * Touch control pad: lower z-index from 1500 → 950 (below modals, doesn't collide with the next-piece HUD spatially) and hide when a tutorial / loading overlay is on screen. Confirmed visually on prod: pad hidden during join modal, visible during tetris phase.
* Row-clear, king-fall, king-captured, king-eliminated, orb-claim, promotion-credit, activity-event drop/promotion sounds were all firing for every player on the world, just at reduced gain. On a busy server (or with a churning bot) that adds up to a constant background of beeps for actions the local player can do nothing about. They now fire ONLY when the local player is the actor. * Row-clear keeps a single beep when a remote clear knocks one of our own chess pieces off — that's a state change we care about — but only for the first iteration of any cascade, never one per cascade step. Toasts for remote clears were already gated; unchanged. * Server: `BoardManager.settleAirbornePieces` outcomes now carry `pieceOwner` so the client can tell whose piece just settled. Client falls back to looking up the owner in `gameState.chessPieces` during a rolling deploy. * King-captured battle overlay no longer pops for two strangers on the other side of the world — only when the local player is captor or defeated. Same for the king-fall sting. * Touch control pad: showing the pad now also pushes the toast container above it, and `showToastMessage` consults the pad on every render so a toast that appears before any pad-visibility change still lands above the buttons. * tests/server/BoardManager.test.js: two new regressions covering the new `pieceOwner` field for both 'landed' and 'fell' outcomes. 411/411 server tests pass.
Adds the drifting Viking longship fleet (server BoatManager, client renderer with sail-mounted advertiser textures), replaces the puffy under-cell clouds with flat wave-foam patches at the water surface, disables the water plane's shadow receive so islands look like they float, and lets knights (plus the cell they stand on) survive island decay so a stranded knight isn't auto-removed when its island loses its path to the king. Also fixes the production "Game initialisation failed" crash: public/js/createFewClouds.js was importing THREE via the limited utils/three.module.js shim (no CircleGeometry export), so the foam constructor threw "(void 0) is not a constructor" and the whole init chain failed. Switched to getTHREE() like the rest of the client code uses. Includes mobile long-press-and-drag for the falling tetromino, tests for the boat manager and knight-survival sweep, and the docs update covering the new sea/boat behaviour.
Two visible follow-ups from the user playing the live build:
1) Cells looked like they were hovering above the sea after the
foam patches dropped down to the water surface. Each
`cloudPuff` group now also carries a tapered brown rock pillar
that descends from the cell bottom (~ -0.47) to the water
surface (~ -2.0), so islands read as actually sitting on the
sea. The foam-pulse animation has been adjusted to scale the
foam meshes individually instead of the parent group, so the
rock stays put. No pathing / chess-logic impact — pure
decoration.
2) Boats were carrying a default striped sail with no ad pipeline.
`pickAdvertiserForBoat` now falls back to a frozen
`PLACEHOLDER_SAIL_AD` ("Your Ad Here →", links to /advertise)
when no paid advertisers are active so every boat carries
something visible + clickable. The client sail texture now
composites the advertiser name into a dark banner along the
bottom (and blits the ad image on top of the stripes when one
is provided), cached per advertiser id+image. A new
`tryBoatClick(mouse)` raycaster is wired into the main click
handler in inputManager — a click on a sailing longship now
opens the advertiser's landing page (or /advertise for the
placeholder) in a new tab. Click handling is
phase-independent so boats work in both tetris and chess
phases.
Includes:
- Updated test for the snapshot's `placeholder` flag.
- Players bible + boats-and-viking-knights docs updated to cover
the new island base, the placeholder sail, and the click-to-ad
behaviour.
- Updated longship movement to wander between random waypoints within a defined area, improving fleet distribution and visibility near islands. - Adjusted boat rendering to include distance-based fading and a retro variant with low-poly models. - Enhanced the advertising system to hold image uploads in memory until payment confirmation, preventing abuse and spam. - Updated documentation to reflect changes in boat behavior, advertising processes, and new rendering features. - Added tests for the new image handling and boat movement logic.
- Revised the advertisement description and tagline on the advertise page to better reflect the new advertising model. - Enhanced the chess interaction logic to include new functions for disposing of chess piece meshes and removing stale pieces from the board. - Implemented a priority move click feature that allows players to execute chess moves even during the tetris phase. - Improved the handling of chess piece captures and king captures, ensuring proper updates to the game state and visual feedback. - Updated the rendering logic for chess pieces to prevent ghost pieces from appearing after captures.
- Adjusted pawn promotion distance from 9 to 8 squares, aligning with new game rules. - Implemented a frozen pawn state that prevents movement and allows for piece deployment from the captured basket. - Enhanced the user interface to include a glowing halo around frozen pawns, indicating their promotion status. - Updated chess interaction logic to facilitate the re-opening of the promotion dialog when clicking on a frozen pawn. - Improved visual feedback for valid moves and captures, ensuring a smoother gameplay experience.
- Removed SendGrid dependency and email service, transitioning to Auth0 for secure sign-in and email handling. - Updated ecosystem configuration to reflect new Auth0 integration and removed outdated comments. - Enhanced user interface for sign-in, replacing the magic link section with a streamlined Auth0 login button. - Improved chess interaction logic to accommodate new authentication flow, ensuring seamless user experience across devices. - Updated documentation to reflect changes in authentication processes and user interface adjustments.
- Introduced a multi-stage Docker build process to create a production client bundle, ensuring only the optimized output is included in the final image. - Updated the Jenkins pipeline to include a dedicated stage for building the client bundle, allowing for early detection of build issues before deployment. - Enhanced the `public/index.html` and `manifest.json` for improved SEO and shareability, including Open Graph and Twitter card metadata. - Implemented static matrix optimization for chess pieces to improve rendering performance during gameplay. - Updated the loading indicator and tutorial message to streamline user experience during game initialization. - Improved security measures by restricting access to sensitive endpoints and refining Content Security Policy (CSP) directives.
- Enhanced the `scripts/deploy.sh` to exclude live runtime state from rsync, preventing overwriting of production data during deployments. - Updated the changelog to reflect the successful production cutover executed on June 2, 2026, including verification steps and manual configurations. - Documented manual steps for DNS, environment variables, and nginx configuration to ensure a secure and smooth deployment process.
- inputManager: sample board X/Z axes at the piece's rendered position instead of the world origin, so "rotate controls with view" follows the camera on both axes (the board sits ~100 cells from the origin, which projected off-screen/behind the camera and broke side-to-side). - world gravity: skip players who are connected or active within a 5-minute grace window, so a just-placed piece no longer drifts a cell on the next minute tick. Gravity now only consolidates abandoned, far-flung territory. Adds two regression tests.
Players can now save their kingdom and resume it on any device with no
email, PII or third party. The browser derives an opaque key from
username+passphrase (SHA-256, never transmitted) into a tetches_auth_key
cookie; the server adopts it as the canonical player id — resuming an
existing account, migrating the current guest kingdom onto it on first
login, or claiming a fresh one. A strict player_<hex> namespace gate
prevents collisions with uuid device ids / ai- bots and blocks claiming
another player's id.
- server/world/World.js: reassignPlayerId() re-keys a player's full
footprint (record, home zone, pieces, cells, turn/disconnect state).
- server/sockets/connection.js: resolvePlayerIdForSocket() adopts the
tetches_auth_key cookie (resume / migrate / claim) before the
anonymous device cookie.
- public/js/auth/{kingdomKey,loginDialog}.js + main-enhanced.js +
unifiedPlayerBar.js: shared login dialog from the welcome prompt and a
player-bar Account row (log in / signed-in + log out).
- server/persistence.js: DATA_DIR honours TETCHES_DATA_DIR for isolated
local testing (production leaves it unset).
Auth0 passwordless email stays scaffolded as a later upgrade. Covered by
21 unit tests (accountLogin.test.js, world.test.js) and verified e2e:
guest -> login migrates kingdom -> reload resumes -> logout = fresh guest.
First-week-live sweep: the next-piece widget's "Click to start tetris turn" was dead (capture-phase stopPropagation missed [role=button] HUD elements), the ghost piece now shows green/red placement legality so first drops stop silently dissolving, dissolve toasts teach the adjacency rule, spacebar matching is tolerant, the redundant "Found clearable" log is gone (~54k lines/8 days) and the stale sendgrid lockfile entry that broke npm ci on deploys is regenerated.
Measure the page-load → play drop-off with PII-free counters (admin endpoint /api/admin/funnel); simplify the first screen to hero image + PLAY NOW with the rules behind a toggle and login clearly optional; remind guests with progress before they leave; stop the player bar auto-opening on phones and quieten the network pill / bottom strip.
Arenas are remote circular regions of the shared world (design doc: docs/battle-mode-design.md). Neutral 2-thick ring (d=32) bounds play and blocks long-range travel; seats are fresh player records so main kingdoms are untouched. Includes seat aliasing for socket routing, an occupied-cell line-scan rewrite (bounding box was O(2000^2) with remote arenas), sweep exemptions for battle seats, snapshot persistence and restore, client dialog/invite links/seat adoption, and a 16-test suite plus live socket E2E.
…LAY click, split lobby Close from Cancel The reported "empty landscape" after starting a battle came from the client deriving its render origin from board bounds when the server sent no centre marker — an arena spawning ~2,240 cells away yanked the derived midpoint to (1120,1036) and re-anchored every mesh out from under the camera. The server now pins board.centreMarker at (0,0) (with snapshot backfill) and the client never guesses from bounds. Also: sea plane follows the camera target so remote arenas sit on water; welcome modal carries the optional name field (no more blocking name dialog) and gates join_game behind PLAY/BATTLE; battle lobby separates Close (keep waiting) from the destructive Cancel; loading/marker console spam quieted. Verified by new unit tests plus a live socket E2E harness (scripts/e2e-battle-flow.js) replaying the exact repro.
…ns leave no trace
… flyover "Start battle" did nothing because the socket bridge's forward whitelist had no battle_* events and battleMode subscribed via onMessage(), which these events never traverse — every lobby update and start died in transit. Fixing that unblocked the agreed redesign: arriving at the site no longer auto-joins the global game (spectator overview + welcome modal), PLAY NOW joins with a sweeping drone flight to your king, and BATTLE goes straight to lobby -> arena with only arena cells rendered (edge of the game = edge of the world), returning to the overview when the battle ends. Battle-only players adopt the gameId without join_game, get a battle session with no kingdom, and are stamped activeBattleId server-side so the ghost sweep can't eliminate them mid-battle. Lobby dialog got a clarity pass (numbered steps, live seats, bot-fill start hints, copyable invite link). 623 tests across 57 suites plus a 48-check live E2E replaying the exact repro with two battle-only sockets.
The harness only read argv[2], so `E2E_URL=... node e2e-battle-flow.js` silently tested the default local server while claiming green against production. Now argv wins, then E2E_URL, then the local default — and the target is printed so a wrong-server run is visible at a glance.
…r; agent gateway (MCP server + Gopher discovery) Battle fixes (all owner repros): - AI chess mover enumerates legal moves per piece instead of sampling random board cells (arena bots found ~0 moves); runner falls back to the other action type in the same tick. - Arena slots stay owned by FINISHED battles until cleanup — reusing the slot mid-linger built new arenas on stale rings/pieces. - Every socket joins the world broadcast room at connect — battle-only guests missed all game_update traffic. - Sessions track ALL of a player's sockets; battle/duel/targeted emits reach every tab; disconnect grace only when the last tab closes. - URL carries ?battle=CODE while seated; invite links make JOIN BATTLE the primary action; different-battle invites no longer hijack a seated player; joining an ACTIVE battle takes over a bot seat. Agent gateway: - /mcp — MCP Streamable HTTP server bridging to loopback Socket.IO (join, state, place, move, full battle suite as tools). - /.well-known/agent.gopher — Gopher-over-HTTPS discovery tree. - docs/mcp-agents.md + public repo Rotwang9000/tetches-agents with examples and MCP-registry manifest. 642 tests / 59 suites green; e2e-battle-flow 63/63; e2e-mcp 22/22.
…per-battle view isolation 3-4 seat arenas fan out with >=5 cells between armies (a CPU pawn could capture on move one), instanceColor is allocated eagerly so cells never lock into the white no-colour shader (three r132 program cache), seat colours flow server->client so armies are distinct, the falling piece anchors and colours off the local seat, and battle/world views no longer leak into each other (duels, activity log, sidebar, orbs, nameplates, toast coordinates). Water is Lambert now - the specular sun blob washed out whole arenas. Dev servers serve source modules even when a stale prod bundle exists on disk.
…daptive bot pacing, 2D lite mode - Battle zones stagger FAN_TANGENT_SHIFT cells left in 3-4 seat games so armies flank, not face - Players can hold seats in several battles; battle_focus routes input to the viewed battle; dialog hub lists battles + shared world with switch/forfeit - Camera flights over 400 units fade-teleport-fade instead of a long blue arc - Bot difficulty per battle (auto/easy/medium/hard); auto retunes bots to the humans' move tempo (pacing.js) - liteMode.js: full 2D canvas client at /2d or on WebGL failure - chess, tetromino ghost, battles, no THREE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.