Commit Graph
47 Commits
Author SHA1 Message Date
jwinterm dacbf356ed fix: close pre-launch review findings and light the premium renderers
Adversarial multi-agent review of the launch candidate produced 22 confirmed
findings (12 more were refuted). This closes the critical, high, and the
mediums/lows that are cheap and real, then does the iso/3D/Tavern art pass.

CRITICAL — unauthenticated remote kill switch
  Socket.IO does not wrap listener invocation, and this process treats
  uncaughtException/unhandledRejection as fatal, so ONE unguarded throw in ANY
  listener exits the server. `socket.emit('debug_ping')` with no argument did
  exactly that, and systemd restarts straight back into it. Every registration
  in handleConnection now goes through a safe-dispatch wrapper that contains
  sync throws and rejected promises to the offending socket; handleDebugPing
  also stops trusting its payload.

HIGH
  - NIP-98 login now binds the signed event to this deployment's host. The path
    check alone let an event signed for any other origin authenticate here, and
    linking ADOPTS the account owning that pubkey — full account takeover.
  - Fog of war is enforced server-side. getState() shipped absolute
    entrance/exit/treasure coordinates and the monster's exact position in every
    frame, with concealment implemented only in the browser. Discovery is sticky;
    the monster is reported only while in view.
  - NODE_ENV must now be explicit and recognized. Unset, every production gate
    read false while DebugManager reported production and silenced logging.
  - Tavern chat applies the lobby's ban list, rate limiter and user_id
    attribution before entering global chat.
  - userRegistry is deleted by socket.id (it was keyed by socket.id and deleted
    by socket.client.id, so nothing was ever freed).

MEDIUM / LOW
  - Payouts: a legitimately split transfer_split kept its transaction hashes
    instead of quarantining already-broadcast funds with tx_hash NULL, and
    provably pre-broadcast failures return to 'pending' rather than needs_review.
  - RateLimiter key parsing fixed — cleanup() freed nothing and expired windows
    never reset, because ids contain colons.
  - Enter no longer submits a paid entry from behind a modal.
  - Lobby chat no longer double-escapes server-escaped text.
  - Rate limits for identity:get / address:prompt / early_entry.
  - Spectators released when a game ends by disconnect, not just settlement.
  - movementManager / addressManager per-socket maps cleaned on disconnect.

Renderers
  Iso: opaque shading instead of translucent fog, additive torch/lantern/beacon
  light pools, crevice AO, embers, cold memory wash, and it stops reallocating
  the whole canvas every animation frame.
  3D: instanced level geometry (3 draw calls), procedural stone/flagstone
  materials, torch lights + shadows, real meshes for stairs/chest/monster
  (they were humanoid capsules), dust motes, wall cutaway, and lighting
  rebalanced now that fog is measured from the camera.

Tavern
  Block height and hash fragments drift around the stage edge and ripple when a
  block lands, fed by a cosmetic chain tip added to the blockheight broadcast.
  Speech bubbles work again (they keyed off a socketId the server stopped
  sending).

126 suites / 1005 tests pass.
2026-07-26 07:29:47 -04:00
jwinterm cbaca7d45b fix: enforce operated product boundaries 2026-07-23 10:44:32 -04:00
jwinterm 89398335c7 feat: productionize paid modes and PvP capture 2026-07-21 20:41:27 -04:00
jwinterm 8fb284f8ed fix(smirk): re-check link status after token rotation (fixes one-click after reload)
Even with the anon_token status query, Smirk one-click stayed disabled after a reload: the server
ROTATES the session token on every resume (sessionManager), so the initial SmirkAuth.checkStatus()
raced the rotation and 403d with the now-stale token, leaving _isLinked=false. Re-run checkStatus
right after the fresh token is persisted (session_token / session_resumed) via _refreshSmirkLinked,
so _isLinked reflects the current token before the user buys — restoring the Smirk payment popup.
2026-07-18 04:37:53 -04:00
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 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 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 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
jwinterm 4ad063f735 Get back into it 2025-09-24 14:06:42 -04:00
jwinterm bd97d9d848 First (untested) payment and db setup 2025-06-01 14:17:29 -04:00
jwinterm f02e16272e refactor frontend and fix debug mode 2025-05-29 13:40:44 -04:00