- 01 Sep, 2026 6 commits
-
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
requestBotMove retried forever on failure and did nothing at all when the engine answered without a move, so a Stockfish hiccup left the player watching the thinking indicator with no way to continue. Now: four attempts with exponential backoff, then a dialog offering to end the game. Also guards against gameState having been torn down mid-request. The engine itself is healthy — verified live: GET /api/chess/bots returns the seven bots the client expects, and POST /api/chess/move answers the start position with d2d4 at depth 3. api/bots.php's paths are correct. Arabic and English strings added for the new message. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
No completed match in production has ever had white_rating_after or rating_change_white written, and 272 of the 290 profiles were still sitting on the default 1200. The complete_match database function does not touch the rating columns, and game.php had calculateElo() and getRatingColumn() helpers that nothing ever called. Elo is now computed in applyRatings(), called from finaliseMatch() after its conditional write has succeeded — so a game can never be rated twice. It writes both ratings before and after and the deltas onto the match, updates the correct per-time-control column on each profile along with the game/win/loss/draw and streak counters, and records a rating_history row per player. This matters beyond the leaderboard: Swiss seeding orders players by rating, so with every rating pinned at 1200 the top-half/bottom-half split in round 1 was arbitrary. Bot games and aborted games are never rated, and a match with the same player on both sides is refused outright. Covered by e2e sections 18 and 19. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
Production carries seven matchmaking_queue rows marked `matched` from May and June, each pointing at a game that finished months ago. The previous commit started honouring `matched` rows so the hand-off survives a re-queue — which, without this, would have sent those players straight into a three-month-old finished match. liveClaimedMatch() now verifies the referenced match is real, still playable, and actually belongs to the caller before acting on it, and deletes the row on the spot when it is not. Self-healing, so the existing rows need no manual cleanup. handleStatus also deletes only the row it consumed rather than every row for that player, and returns opponent_id alongside the colour. Covered by e2e section 17: a stale claim is refused and cleared, queueing past one works normally, and a genuinely live claim is still delivered with the correct opposite colour. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
api/cron.php still exists as the belt-and-braces tick, but an event must not depend on someone remembering to wire one up — especially not the one running on Saturday. The engine now also advances off ordinary traffic, the same way the matchmaking queue already sweeps its own stale rows: - browsing the tournament list starts any tournament whose start time has passed - a player opening their game, or checking their pending games, forfeits an opponent who never turned up and closes the round if that was the last board Both are idempotent and throttled (once every 15-20s per tournament, via APCu or a temp file), so they cost one query every few seconds rather than one per request. A game that has actually begun is never forfeited by this path — the server clock settles those. Also fixes tournamentEnvInt: `getenv($k) ?: $default` silently discarded a configured value of "0", because "0" is falsy in PHP. tests/run.sh now clears its ports before starting and aborts if a server fails to bind. A stale server from an earlier run had been serving the previous revision on those ports, so the suite was quietly testing old code — the run looked like a product failure and was not one. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
Mahmoud Aglan authored
Four independent defects each broke competitive chess on their own, and they compounded. Verified against live production (Supabase, the Swiss API and the CapRover deployment), not inferred from code alone. Full diagnosis in FIX_PLAN.md. A. Match completion was rejected for every decisive game. The client sends result 'win'|'loss'|'draw'; game.php's $validResults did not contain 'win' or 'loss', so every won or lost game got a 400. That list was also wrong in its own right — timeout_white, abandon_white, resign_white, checkmate_* and bot_* are not in the public.match_result enum. And handleComplete ignored the write's return value, so a Postgres rejection was invisible and the endpoint still reported success. Consequence: no rating, no coins, no XP, matches stuck in_progress forever, tournament results lost, and the winner shown a fabricated +12. The client now reports only *how* the game ended. The server derives the winner from the final position (the mated side is the side to move), from who resigned, or from whose clock expired, validates against the real enum, and verifies the write landed. A losing player can no longer report a win. B. Both players could be assigned White — "playing against yourself". `playerColor = color || 'w'` defaulted both players to White whenever a caller supplied no colour, and the poll only ingests the opponent's move while it is *not* your turn, so two Whites never saw each other and each played a private board. The async correction also wrote gameState.playerColor while the clock closures still read a stale local copy. Colour now has one source of truth. game.php returns my_color computed server-side; the board stays inert until the server confirms it. Fixed the callers that passed no colour (refresh recovery, group invites — which also lacked mode:'live' and so started a bot game) and stopped lobby.js guessing from isHost. C. Finished matches were never released. unmountGame never tore down the match session, so localStorage kept the recovery key; combined with A, every app start dragged players back into a dead game with no colour, straight into B. D. Hand-offs were destroyed. matchmaking.php deleted all of a player's queue rows including one already matched and carrying a match_id, so the waiting player never learned their match and their opponent sat alone. Tournament create-or-join did a read-then-insert with no lock, so both paired players could create their own match. Match ids are now derived from (tournament, round, board): both clients compute the same id and the primary key decides, so exactly one match exists per pairing. E. There was no tournament engine at all. Nothing created a Swiss tournament, consumed auto_start, generated pairings, advanced a round or computed standings. api/swiss.php sent no Authorization header, so every call to the external pairing service returned 401 and each getter swallowed it and returned an empty array — which is why standings and pairings have always rendered blank. includes/tournament-engine.php is a native Dutch-system Swiss engine with no external dependency: backtracking pairing that never repeats an opponent, FIDE colour allocation, byes, Buchholz cut-1 / Buchholz / Sonneborn-Berger tiebreaks, automatic round advance and final placings. api/tournament-admin.php creates and starts tournaments; api/cron.php is the scheduler that acts on auto_start and forfeits no-shows. Also fixed - Server authority: turn ownership, move-count monotonicity, FEN transition sanity and a server-side clock, so a client can no longer overwrite the position or report its own remaining time. - Disconnect detection was dead: match-live marked the opponent active on every poll, and match-session's abandon branch was unreachable behind the disconnect branch's latch. Heartbeats are now recorded per player in game_state. - Draw acceptance required no offer from the opponent — a way out of a lost game. - Removed the in-process HTTP call back into our own Apache for tournament reporting (deadlock risk, and it lost results silently when it failed). - Three time controls in the picker (blitz_5_5, rapid_20_0, classical_45_45) are not in the time_control enum; choosing one created no match. - Auth did an upstream GoTrue call plus a ban query on every request, including the 2s poll. Now verifies HS256 locally when the secret is set, otherwise memoises briefly. - curl_close() removed repo-wide: deprecated in 8.5, where its notice lands in the middle of every JSON body. Dockerfile now turns display_errors off. Tests: tests/run.sh — 5 suites against a disposable local Postgres carrying the production schema. Covers 2-100 player tournaments, 167 real master-game move transitions (no legal move is ever rejected), the pairing race, JWT verification, and a full two-client game and tournament over HTTP. Co-Authored-By:Claude Opus 5 (1M context) <noreply@anthropic.com>
-
- 15 Aug, 2026 1 commit
-
-
Mahmoud Aglan authored
-
- 10 Jul, 2026 1 commit
-
-
Mahmoud Aglan authored
Bug 1: Resign not detected - endGame now skips redundant complete API call when server already completed the match (resign/draw/abandon via poll). Bug 2: Players seeing themselves as opponent - verify and correct color from authoritative server match data on game start. Bug 3: Checkmate not appearing - send final move to server BEFORE calling endGame so opponent receives the checkmate FEN position. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
- 03 Jul, 2026 32 commits
-
-
Mahmoud Aglan authored
- tournaments.php: exclude cancelled tournaments (neq.cancelled filter), hide completed tournaments older than 3 days - hub.js: treat 'draft' status same as 'registration' — show register button, green status pill, include in "registration open" filter Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- tournaments.php: remove non-existent 'ends_at' column from select (caused PostgREST error → empty tournaments list) - i18n.js: add all missing tournament hub keys (my_tournaments, no_my_tournaments, format_*, players_label, etc.) + common keys (all, retry, failed, coins, play, minutes_abbr) Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The .env file was not being included in Docker builds (gitignored), causing all API calls to fail with 401 after each redeployment. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Fills the gap between --bg-card (#1a1a2e) and --bg-elevated (#1e1e3a) for JS modules that reference this token. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Sanitize time_control to rapid_10_0 if not in the allowed list. Prevents garbage values from reaching the database. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- multiplayer.php: verify join succeeded after update (race detection) - table.js: show My Games button for all games, not just chess - i18n: add game.my_games key (ar: مبارياتي, en: My Games) Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- friends.js: invite dialog now shows all 4 games in a 2x2 grid - friends.php: create/check/accept/decline backgammon invites - lobby.js: route to backgammon-game scene, show correct icon/label/color - Time options hidden for non-chess games Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- tournament-arena, tournament-lobby, tournament-live: export unmount - group-chat: export unmount to clear realtime sub + invite timer - social/mod.js: register unmountChat and unmountGroupChat - Prevents interval leaks when navigating away from these scenes Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Domino: - dealAndSyncToServer: retry once on failure, abort to room on 2nd fail (#100) - syncPassToServer: only increment moveCount after success (#41) - drawFromBoneyard: debounce with drawInFlight flag (#42) - endMatch: guard empty players array, fallback to match IDs (#209) Backgammon: - createGame: fallback to sheshbesh if variant key missing (#26) - handleServerState: selective merge instead of Object.assign (#56) Core: - multiplayer.js: mark startDisconnectWatch as deprecated (#229) - core.css: replace hardcoded HUD values with token variables Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
The client sends `after: lastTime` to fetch only new messages since the last poll. Server now handles this with `created_at gt.{after}` filter. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- auth.php: eliminate double token verification (decode JWT payload directly) - auth.php: cascade delete related data on account deletion (blocks, daily_claims, challenges, achievements, group_members) - auth.php: add server-side username regex validation - register.js: add client-side username character validation - profile/view.js: show "Unrated" for players with 0 games at 1200 rating - i18n: add auth.invalid_username and profile.unrated keys Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 1 (Chess Multiplayer Bulletproof): - net.js: check content-type before JSON parse, clear error on HTML responses - modal.js: stack-based resolve — stacked modals no longer hang - match-session.js: fix null dereference after destroy, remove listener on cleanup - match-session.js: only fire onConnectionRestored after actual disconnect - lobby.js: prevent duplicate startGame calls with gameStarted flag - lobby.js: add pollInFlight guard, clear timeouts on unmount - lobby.js: derive color from match data when undefined (friend invite fix) - scene.js: queue navigation during transition instead of dropping it - chess/game.js: pass timeControl to result scene for correct rematch Phase 5 (Core UX): - table.js: hide "My Games" button for non-chess games (no history scenes) - queue.js: add polling overlap guard - profile/view.js: show "Unrated" for new players instead of 1200 - i18n: add profile.unrated key (ar/en) Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Created core/config.js with SUPABASE_URL, STOCKFISH_URL, DEFAULT_RATING - login.js, register.js, realtime.js now import from shared config - chess/game.js and bot-select.js use STOCKFISH_URL constant - multiplayer.js uses DEFAULT_RATING from config - ludo/game.js: named TURBO_SPEED constant - table.js: removed dead `disabled = false` code path Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Updated tokens.css with aligned color values and added missing tokens (bg-panel, bg-inset, bg-dark, gold-dark, purple-light, violet, green, green-light, amber, red-light, red-soft, text-dim, text-light, info). Updated theme.js applyColors map to support all new tokens via admin branding panel. Converted ~85% of all inline hex colors across 50+ JS files to use var(--token) references. Remaining ~160 are canvas colors (which can't use CSS vars) and unmapped game-specific colors. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
27 files converted to use the emoji(slot, fallback, size) function from core/theme.js, enabling full emoji customization via the admin branding panel. Covers core modules, all 4 game scenes, play flow, social, rewards, org, and tournaments. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Replace all 40+ remaining Arabic strings in tournament-detail.js. Add tournament format/prizes/registration/standings/rounds keys to both ar and en dictionaries. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Replace hardcoded Arabic in tournament-arena.js (7 strings), tournament-detail.js (10 strings), tournament-live.js (8 strings). Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
All UI text across 57 files now goes through the i18n system (core/i18n.js). Added ~120 new translation keys covering: auth, daily rewards, challenges, ranks, shop, groups, tournaments, puzzles, orgs, emotes, and backgammon doubling. Both ar and en dictionaries are complete and in sync. Data strings (country names, FIDE titles, variant names, puzzle themes) are intentionally left as-is — they use name/nameEn data pattern. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 2.4: multiplayer.php now routes to correct table for all games (chess→matches, domino→domino_matches, backgammon→backgammon_matches, ludo→ludo_matches). Validates game_key against allowed list. Phase 4.3: handleServerState in backgammon only copies specific fields (state, dice, turn, gameOver) instead of Object.assign which clobbered local methods and properties. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 1.13: add unmountGame export for chess — stops clock, destroys board, nulls gameState on scene exit. Phase 5.1: notification items now navigate to relevant section on click (friends, play, tournaments, achievements based on type). Phase 6.6: remove remaining stopDisconnectWatch calls from domino/ludo since disconnect detection is now fully handled by match-session.js. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 7.1: add 30+ missing i18n keys for queue timeout, game states, achievements, and chat. Replace hardcoded Arabic in chess game scene with t() calls. Phase 7.3: replace hardcoded hex colors in chess HUD and draw dialog with CSS variables (--text-primary, --bg-surface, --success, --error). Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 1.6: add pollInFlight guard to match-session.js so overlapping polls are skipped when server is slow. Phase 2.5: room code generation retries up to 5 times on collision. Phase 2.6: join_room uses service key, checks if room is full, prevents double-join, and auto-starts when player count is met. Phase 3.1: dealAndSyncToServer retries on failure with toast notification. Phase 3.3: drawFromBoneyard syncs once after all tiles drawn instead of firing N parallel requests. Phase 3.6: check-invites uses PostgREST cs filter on JSONB players array instead of scanning all waiting matches. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 4: getPipCount now accepts variant param (fixes wrong pip count for thirtyone variant), backgammon quit button shows confirmation dialog in live mode. Phase 3: syncPassToServer no longer pre-increments moveCount before network success, winners array guards against empty state.players. Phase 2: ludo bot "thinking" indicator uses dedicated .pp-status span instead of clobbering the player name. Phase 6: chat.php unread/recent queries batched into single DB calls instead of N+1 per friendship. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 5: queue 60s timeout with retry prompt, match history accepts game_key, spectate routes to correct game scene, shop shows "owned" indicator, profile shows dash instead of 1200 for unrated players. Phase 6: remove duplicate disconnectWatch calls (use match-session only), fix auth.php double token verification with requireAuthUser(), cascade delete related data on account deletion. Phase 1: fix opponentRating using actual rating in live mode instead of botElos lookup. Fix achievements recursive mount — update in-place instead of re-calling mountAchievements. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- friends.js: add unmountFriends() to clear refresh/invite timers - play/mod.js: register unmountLobby for game-lobby scene - social/mod.js: register unmountFriends for friends scene - net.js: add 10s request timeout via AbortController - net.js: add mutex for token refresh (prevents duplicate refresh calls) - notifications.js: don't auto-mark-read on mount; add explicit button - chat.js: pass lastTime as 'after' param for incremental fetch Fixes WTF #38, #63, #66, #69, #91, #175 Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- modal.js: dismiss existing modal before showing new one (prevents stacking) - scene.js: call unmount on current scene during replace() and switchWorld() - chat.js: remove || true from scroll condition (only auto-scroll on new msgs) - shop/browse.js: check gems affordability alongside coins - ludo/game.js: fix undefined isLoser — use isLastPlace - backgammon/rules.js: fix VARIANTS.standard crash — use VARIANTS.sheshbesh - backgammon/game.js: sanitize opponentName and avatar URL (XSS prevention) Fixes WTF #22, #25, #26, #59, #68, #71, #113 Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Phase 1 fixes: - lobby.js: guest waits 2.5s before entering game (gives host time to detect) - challenge.js: validate match_id exists before navigating to lobby - match-session.js: remove visibilitychange listener on destroy (memory leak fix) - match-session.js: only fire onConnectionRestored after actual disconnection - net.js: handle non-JSON server responses gracefully (nginx 502 pages etc) - result.js: pass actual timeControl to rematch instead of hardcoded rapid_10_0 - Remove dead chess/logic/live.js and ludo/logic/live.js (unused) Fixes WTF #15, #19, #23, #24, #35, #37, #54, #150 Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Reject invalid time_control values before DB write - Reject invalid game_key values before DB write - Also added missing DB indexes and reward_config rows via migration Fixes WTF #schema-2-3, Phase 0.7-0.9 Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- Replace wildcard CORS (Access-Control-Allow-Origin: *) with domain whitelist across all 37 API files via shared includes/cors.php - friends.php: sanitize PostgREST filter inputs (strip special chars from search) - friends.php: validate UUID format for profile ID lookups - friends.php: verify user is invite target before accept/decline (domino, ludo, chess) - config/constants.php: read secrets from .env file or env vars (no more hardcoded keys) - Add .env to .gitignore Fixes WTF #5-6, #9-11 Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
- handleGameMove: verify caller is a player in the match before allowing moves - handleResign: verify participant before allowing resignation - handleDraw: verify participant + use merge_game_state RPC (preserves heartbeat data) - handleComplete: verify participant + validate winners are actual match players (prevents coin exploit) - handleFindActiveMatch: restrict to own user only (prevents info disclosure) - Validate result enum values in handleComplete Fixes WTF #1-4, #46 Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-
Mahmoud Aglan authored
Prioritized plan to go from 240 WTFs to all 4 games working in multiplayer. Phase 0 locks down security, phases 1-4 bring each game online, phases 5-7 polish UX/stability/theming. Co-Authored-By:Claude Opus 4.6 <noreply@anthropic.com>
-