Skip to content

Develop - #1

Merged
Rotwang9000 merged 31 commits into
mainfrom
Develop
Jul 3, 2026
Merged

Develop#1
Rotwang9000 merged 31 commits into
mainfrom
Develop

Conversation

@Rotwang9000

Copy link
Copy Markdown
Owner

No description provided.

S. K. Rotwang MMMMMMMMM 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.
… 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
@Rotwang9000
Rotwang9000 merged commit 436c75a into main Jul 3, 2026
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant