Commit Graph
53 Commits
Author SHA1 Message Date
jwinterm ea23ba404d fix(fx): drop per-step red monster flash; big banner on descend/treasure
Red pulse: _fxOnPlayerMoved fired a full-screen red FX.flash whenever a monster was within 3
tiles (throttled 550ms) — the monster usually IS within 3 tiles, so it pulsed on nearly every
step. Removed it; the monster is already on-screen, so proximity needs no flash. FX.flash is now
reserved for genuine one-shots (win/lose).

Descend feedback: taking the stairs only logged a tiny chat line. Add showEventBanner() — a big
centered pop-in/hold/fade banner over the game view (pointer-events:none, auto-removed) fired on
descend ("LEVEL n / N") and treasure ("TREASURE SECURED"), with a soft one-shot colored flash.
Chat lines kept as the log trail.
2026-07-13 07:58:43 -04:00
jwinterm ce0ad5c0d0 fix(queue): free "Next block" no longer pops the payment modal
The consolidated entry modal Free Play + Next block path emitted a bare join_queue with no free
intent, so handleGameQueue ran full payment eligibility and (on a mixed paid+free instance) fired
show_payment_options — the old "Choose How to Play" modal appeared on top of an already-queued free
game. Thread the free flag end-to-end, mirroring auto_start{free}: join_queue{free} -> handleJoinQueue
-> handleGameQueue (skips _checkPaymentEligibility when free+freePlayEnabled, tags the queued entry
free) -> queueManager.addPlayer stores free -> startGamesForWaiting/startGameImmediately pass
{ forceFree } into processGameStart, so the block-landing start takes no credit/payment and lands on
the Pleb board. Ranked queued path (plain join_queue) unchanged. Suite 62/435 green.
2026-07-12 19:58:49 -04:00
jwinterm 6879d3d943 fix(ui): clear transient entry/starting console lines once game is live
The "Free game — dropping in…" (begin) and "Starting game…" (onGameStart) console lines were
permanent log entries, so a running game sat next to text reading as if it were perpetually
starting. Tag both as transient: entry-progress is removed when onGameStart fires (kept during
the real block-wait for the queued variant); the game-start "Starting game…" line is removed once
Game.startGame succeeds. The server status_update "Game started! Escape before the next block!"
remains the standing confirmation.
2026-07-12 19:45:08 -04:00
jwinterm 8a3b254935 fix(ux): honest block-time wording + smooth (lerped) SP camera
- Modal: "Safe — full block window" overstated it (blocks arrive randomly, Poisson). Now
  "Start fresh · blocks land at random" / "Jump in · race the time left".
- Camera: was snapping to the player every frame (jerky on canvas resize / level teleport).
  Now glides (lerp 0.3) and only snaps on a big jump; snap-centers on new-game start.
2026-07-12 15:38:53 -04:00
jwinterm 0dbdb387b4 feat(entry): one consolidated Enter-the-Dungeon modal (kill the double-modal)
Replaces the timing-only modal (which then triggered the server payment/options modal) with
a single dialog: a timing toggle (Next block / Right now) + stakes cards (Free / Ranked +
Buy credits). Each card emits the FINAL intent so the server never re-prompts:
- Free -> auto_start {free:true} / join_queue  (free:true skips the options modal)
- Ranked + credits -> auto_start / join_queue (uses a credit)
- Ranked no-credits / Buy -> PaymentUI.show() (the real payment, not a redundant choose)
Nothing starts until a card is clicked, so the game never renders behind the modal.
2026-07-12 13:12:34 -04:00
jwinterm dd7d70aec8 fix: fog-of-war entities, multi-level descend feedback, camera centering, fewer levels
- FOG: sceneFromGameState only emits exit/entrance/treasure/items once their cell is EXPLORED,
  and the monster only while IN VIEW — no more exit stairs leaking through the fog.
- DESCEND FEEDBACK: reaching a non-final exit descends (multi-level) and used to silently dump the
  player at a new entrance (read as a bug). Server now sends depth/maxDepth on game_event; the
  client shows "Stairs down! Level N of M — keep going". Also lowered level counts (WOW 4->2,
  BTC 8->3, XMR 2->1) — clearing 4-8 levels in one block was unwinnable; kept modest until proper
  multi-level calibration + UX.
- CAMERA: the host filled the very-tall #game-display so centering put the player low; it's now a
  bounded viewport (min(100%,78vh)) centered in #game-display, so the player stays on-screen-centered.
Suite 62/435 green.
2026-07-12 11:50:40 -04:00
jwinterm 31b4c3ca5d feat(entry): START GAME shows the wait-vs-drop-in choice for free games too
Previously the timing-choice modal (Start Now / Wait for Next Block) only appeared for
paid-credits players; free players got a silent immediate drop-in. Now it shows whenever
the game would start instantly:
- Server getGameModeInfo exposes freePlayEnabled (was server-only) so the client knows
  free play is available even on a paid instance.
- Client (inputHandler) gates the modal on free-play-available OR paid-credits-with-credits;
  the modal is purely a TIMING pick (auto_start vs join_queue) — payment method is unchanged.
- Modal cost text reflects free play.

Both waiting animations are untouched: they render on the ROT display via WaitingScreenAnimator
using the base GameTiles, independent of the render-kit packs (which are disabled for SP anyway),
so they work regardless of visualization style. Suite 62/435 green.
2026-07-12 10:44:31 -04:00
jwinterm 20cbdd364a fix(smirk): sign first, grant Nostr scope only on demand — one approval for returning users
Previously always called getNostrPublicKey() then signNostrEvent() = two approvals every login. Now signNostrEvent() runs first; only if the wallet reports the origin lacks the Nostr scope (NOT_AUTHORIZED, thrown without a popup) do we call getNostrPublicKey() to grant it and retry. First-time still does the one-time scope grant; returning users get a single approval. (The double PASSWORD is the wallet's autoLockMinutes=0 default and is user-configurable, not dapp-controllable.)
2026-07-10 11:07:11 -04:00
jwinterm 65305bf7de fix(smirk): grant the Nostr scope via getNostrPublicKey() before signNostrEvent()
The Smirk dapp API requires the origin to hold the Nostr scope before signNostrEvent(); without it the wallet throws 'Origin lacks the Nostr scope — call getNostrPublicKey() first'. getNostrPublicKey() establishes that grant (prompts once), so call it first in the NIP-98 path.
2026-07-10 10:29:52 -04:00
jwinterm 057767152c fix(smirk): fall back to legacy connect() on a locked signNostrEvent (restores unlock-popup path)
Only an explicit user-cancel skips the fallback now — a LOCKED signNostrEvent again tries connect(), which opens the unlock popup on capable builds. My earlier refinement had removed this path.
2026-07-10 09:55:01 -04:00
jwinterm 7e7e66e887 feat(auth): sign-in-with-wallet — adopt an existing wallet-linked account instead of erroring
A Smirk wallet that already owns another account no longer fails with 'already linked to another account'. Ownership is cryptographically proven, so it's a login: the server re-points that account to the current socket and returns its session token; the client persists it and reloads to re-establish as that account. Fixes a returning user being unable to sign in from a fresh browser session.

NOTE: touches the identity/session path — verify live (sign in with a wallet already linked to another account -> lands on that account with its address/credits).
2026-07-10 09:35:08 -04:00
jwinterm a243dd8fc6 fix(ui): real right gutter so play-area focus outline shows on the right; Smirk flow
Play area: add a 6px right gutter on .container (border-box) so the outward yellow focus outline isn't clipped by overflow:hidden at the viewport edge — the inset-outline attempt got clipped in Chromium. Revert to an outward outline (ui.js).

Smirk: on the NIP-98 path, only fall back to the legacy connect() when the wallet genuinely lacks signNostrEvent — a LOCKED or user-cancelled signNostrEvent now surfaces instead of triggering a second unlock prompt. (The unlock-popup-on-locked behavior itself is a wallet-build capability, added to smirk-monorepo 2026-07-09; builds before that throw LOCKED and get the friendly retry hint.)
2026-07-10 09:11:07 -04:00
jwinterm a05a10efd7 fix(ui): tavern floor, play-area focus border, and Smirk locked-wallet UX
Tavern: revert the per-cell 'varied floor/wall' tiles to coherent single tiles — mixing wood/stone/plank per cell read as visual noise (assetPacks.js).

Play area: draw the keyboard-focus outline inset (outline-offset -2px) so the yellow border shows on the right edge, which the container's overflow:hidden was clipping (ui.js).

Smirk: a locked wallet now shows a friendly 'unlock in the extension, then Retry' hint instead of a red error, and the LOCKED error code is preserved through connect()/signMessage(). Current Smirk builds auto-open the unlock popup on connect(); older builds throw LOCKED, which the Retry button re-invokes (smirkAuth.js).
2026-07-10 08:46:38 -04:00
jwinterm d0d327f3ca feat(auth): NIP-98 nostr sign-in for Smirk; fix SHA256 fallback + /status BOLA
Adds schnorr (BIP-340) NIP-98 verification (kind 27235) as the preferred Smirk login, with the legacy Ed25519 wallet-message path kept as a feature-detected fallback for wallets without signNostrEvent. New src/utils/nip98.js verifies securely: rebuilds a clean event, independently recomputes getEventHash and compares to event.id (defeats nostr-tools' verify-memo footgun), then schnorr-verifies; enforces kind, created_at freshness, and exactly-one u/method/challenge tags bound to the server nonce.

auth.js: /verify branches on body.event (NIP-98) vs {publicKey,signature,challenge} (legacy). Challenge consume is now atomic single-use (UPDATE ... WHERE used=FALSE AND expires_at>NOW() RETURNING) before verification. Removed the deprecated SHA256(challenge) pre-hash fallback (past its removal date). /status is now session-gated (X-Session-Token vs users.anon_token; 401/403 on mismatch) — closes the unauthenticated per-socketId disclosure. Client smirkAuth.js: feature-detects signNostrEvent, signs the NIP-98 event, falls back to legacy; checkStatus() now sends the session token.

Adds nostr-tools ^2.23.9 (works under CJS in prod; test loader falls back to its self-contained bundle under Jest). New adversarial suite test/smirkNip98_ultra.test.js (13 tests: tamper/expiry/wrong-tag/bad-sig/memo-bypass/replay). Suite 47/330 green.
2026-07-09 22:19:30 -04:00
jwinterm a62eefb47b fix: resolve security/correctness review + AAA graphics pass
Backend (Tier 0/1/3): single-payout double-pay -> needs_review; admin-retry tx_hash guard; UNIQUE index on games.payment_id (migration 023) + atomic claim; reconnect no longer resets block-timeout; completeGame keys on user_id+dungeon_seed (not socket_id); token-gated /api/user/:socketId/* endpoints; payout min/max enforcement; orphaned-active-game boot recovery; sessionManager cleanup FK fix; XFF rightmost-hop; global uncaughtException/unhandledRejection guard; server binds 127.0.0.1; provably-fair client-seed (HMAC) + RNG [0,1) bound; difficulty preset ranges reach the digger; chat no longer leaks full socket.id; removed dangerous-keyword SQL theater.

Match mode (Tier 2, still MATCH_ENABLED-gated): fixed match_queue/match_move crash paths + scheduler dead-code + leaked ceiling timer; pot collected before start with abort+refund; BigInt/basis-point fee math, capped payout; no-address winner claimable; winnerId==placement[0]; finalize schema + payout-first isolation; reconnect re-key with ownership; refunds on all cancel paths; real countdown; map/timer cleanup. +16 tests.

Graphics: new overlay FX layer (torch light, vignette, embers, sparkle/burst/flash/shake, self-suspending rAF); gameplay juice; atmospheric screens; CSS design-token chrome with framed modals; richer Kenney tile renderer; imported Kenney dungeon/interior sheets + fx particles.

Verification: 45 suites / 312 tests pass; 40 changed JS files parse; 20 server modules load clean; no regressions.
2026-07-09 17:46:58 -04:00
jwinterm be780c17e6 feat: use character identity in single player 2026-07-06 03:16:26 -04:00
jwinterm 454e6ab69a feat: add tavern render kit and character entitlements 2026-07-05 20:57:46 -04:00
jwinterm 63e3006aca launch features: win feed, public stats, share-a-win, live CTA
Four pre-launch additions to make the game feel alive and shareable:

- Live win feed: server broadcasts 'win_feed' on every escape; client shows a
  transient toast ('X escaped with the bag') so the room feels active.
- Public stats: GET /api/stats (10s-cached) returns games + escapes in the last
  24h and total paid out; a status-area strip polls it for social proof.
- Share-a-win: game-over screen gets 'Share to X' + 'Copy link' buttons pointing
  at the /verify page, which now renders OpenGraph/Twitter-card meta (brand OG
  image per instance) so shared links unfurl with a preview.
- Land-on-action: a dismissible 'N games live now — watch' CTA appears on connect
  when games are in progress, opening the existing spectator panel. Non-intrusive:
  hidden while playing/spectating and never hijacks the canvas.
2026-06-01 09:17:48 -04:00
jwinterm e6936f47d9 feat: network-aware currency label (sXMR), game name, and Smirk gating
- Currency display is now network-aware: XMR on a test network shows sXMR
  (stagenet) / tXMR (testnet) so it's clear the coins aren't mainnet; mainnet
  XMR shows XMR, Wownero stays WOW. A fork running mainnet automatically shows
  XMR. cryptoType (used for decimals/QR asset) is unchanged.
- Product name (Monerogue/Wownerogue) is derived from the currency and no
  longer hardcoded: the chat "Connected to <name> server" message, payment
  descriptions, and the verify page title all follow it.
- Smirk is forced off on any test network (stagenet/testnet) regardless of
  SMIRK_ENABLED, since Smirk only works on mainnet — so forks don't have to
  remember the env flag.

New shared helpers (gameNameFor, currencyLabelFor, isTestNetworkFor); exposed
via game_mode_info (currencyLabel, gameName). Full suite: 22 suites / 159 tests.
2026-05-31 15:57:59 -04:00
jwinterm 7581ba79cb refactor: frontend de-spaghetti (dup declares, dead code, log gating)
- Remove the duplicate _gameMode declaration in the client SocketHandlers
  object literal — it was declared twice, so the later `null` silently
  shadowed the earlier `'free'`. Now declared once with the effective value.
- Remove the dead SocketHandlers._appendMessage calls in smirkAuth.js; that
  method never existed, so the guard always fell through to the working
  .text() append. Keep the safe append.
- Gate broadcastManager's high-volume per-event logs (user count, chat message
  incl. message/socketId PII, game update, game start) behind debug mode;
  previously they logged on every event regardless of NODE_ENV.

Full suite: 21 suites / 155 tests.
2026-05-31 14:39:12 -04:00
jwinterm a664a8bbce security: escape XSS sinks client-side and add CSP + security headers
The client trusted the server to pre-escape chat content and rendered
user-supplied fields via .html()/innerHTML — one server regression away from
stored XSS, and the admin panel injected user-controlled payout addresses
into its authenticated DOM.

- Add a single shared escapeHtml helper (escapes all five HTML-significant
  chars including both quotes); leaderboard now delegates to it.
- Escape chat username + message in onChatBroadcast/onChatHistory.
- Escape user-controlled fields in the admin panel (payout address, socket
  id, player name, status) and make its local escapeHtml quote-safe so values
  in title="..." can't break out.
- Add CSP plus X-Content-Type-Options, X-Frame-Options and Referrer-Policy
  headers via middleware. connect-src 'self' keeps Socket.IO working;
  removing 'unsafe-inline' (nonces) is tracked as a later cleanup.

Full suite: 13 suites / 119 tests.
2026-05-31 09:40:34 -04:00
jwinterm 82fdcc0203 update sockethandlers 2026-04-18 19:11:22 -04:00
jwinterm f2b107fc5f fix stale smirk and amount bug 2026-03-01 13:56:35 -05:00
jwinterm a61851b0f7 add smirk payments and leaderboard and diff tuning 2026-02-20 13:54:36 -05:00
jwinterm 27660d6948 Fix rewards, make tx hash clickable, add win payout notification, and add entry choice modal 2026-02-20 12:34:21 -05:00
jwinterm 1a714fd69d fix credits display 2026-02-20 09:36:55 -05:00
jwinterm ee5a037b84 fix credits logic 2026-02-20 09:21:13 -05:00
jwinterm 320d1a3b1f fix admin panel and credits package 2026-02-17 20:02:22 -05:00
jwinterm a56063867e fix: chat scrollback and admin control 2026-02-03 12:02:52 -05:00
jwinterm 10dfd932d8 fix smirk auth 2026-02-02 17:17:10 -05:00
jwinterm 6cf3961da0 feat: add smirk toggle and more admin capabilities 2026-02-02 08:42:39 -05:00
jwinterm d27df3b6cf Feat: add IP-based rate limiting and house balance thresholds 2026-02-01 17:38:30 -05:00
jwinterm 7573c300cd fix: smirk modal 2026-01-29 08:46:52 -05:00
jwinterm 2286d8deff Feat: add smirk login
Fix: add security hardening and transaction database commits
2026-01-28 17:21:53 -05:00
jwinterm 9330b3b943 Fix direct payment game start, reduce prod logging, QR improvements
- Fix game start failure in direct payment mode when payment confirms
  before mempool detection (add player to queue if not already there)
- Fix debug logging in production: correct indentation in debugRegisteredUsers,
  swap console_logging config (false in prod, true in debug)
- Add close button to QR code container
- Auto-hide QR on payment detected, confirmed, and game start
2026-01-13 16:04:50 -05:00
jwinterm f18fdf3802 fix awaiting next block before payment addy or payment 2026-01-07 17:07:10 -05:00
jwinterm aec1c71a8c add early entry and help screens for new users 2025-12-26 10:21:25 -05:00
jwinterm b062a1a5fa debug payouts 2025-12-24 20:54:07 -05:00
jwinterm 2c781ec043 fix payout calcs 2025-12-24 20:03:26 -05:00
jwinterm e218fbf67b fix payment UI 2025-12-24 17:24:04 -05:00
jwinterm 7d701fe01b add backup scripts and improve docs and .env.example 2025-12-22 16:29:32 -05:00
jwinterm 7f91d83308 fix monster bug and let spectators join pending games 2025-12-22 11:25:22 -05:00
jwinterm 24f46fa35e fix chat and payments and add spectator mode 2025-12-22 10:36:46 -05:00
jwinterm fbf5bd5b17 add stagenet warning and increase diff of game and also add modes 2025-12-21 15:10:40 -05:00
jwinterm deca4a4685 add db commands and test payout (worked!) 2025-11-23 13:33:04 -05:00
jwinterm fa61d60bc0 Limit payment requests 2025-09-30 07:03:41 -04:00
jwinterm 4e7a773496 add address modal and try to fix payout 2025-09-29 17:40:59 -04:00
jwinterm 9a4911f410 fix audio and add new audio and fix qr and db index 2025-09-28 10:43:48 -04:00
jwinterm f8241dd309 Continue working on payment logic 2025-09-27 11:21:11 -04:00
jwinterm e3915dcde2 Begin payment logic and tests 2025-09-27 08:45:22 -04:00