Commit 7372dc29 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: chess resign/draw sync — opponent now sees both actions

- match-live.js: always forward poll data to game (not only on move_count change)
  This was the root cause — resign/draw don't increment move_count, so the
  game never processed the status change or game_state update
- game.php: all match operations now use supabaseService() to bypass RLS
  (handleGameMove, handleResign, handleComplete all failed silently with user token)
- Add dedicated 'draw' action in game.php that atomically marks match completed
- Draw acceptance now calls action:'draw' instead of writing game_state only
- handleResign validates match isn't already completed (prevents double-resign)
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent d63a4517
......@@ -27,6 +27,9 @@ switch ($action) {
case 'resign':
handleResign($db, $userId, $input);
break;
case 'draw':
handleDraw($db, $userId, $input);
break;
case 'complete':
handleComplete($db, $userId, $input);
break;
......@@ -96,6 +99,7 @@ function handleGameMove($db, string $userId, array $input): void {
$matchId = $input['match_id'] ?? '';
if (!$matchId) jsonError('match_id required');
$sdb = supabaseService();
$update = ['updated_at' => date('c')];
if (!empty($input['fen'])) $update['current_fen'] = $input['fen'];
......@@ -107,7 +111,6 @@ function handleGameMove($db, string $userId, array $input): void {
// Merge game_state instead of overwriting — preserves emotes, draw offers, etc.
if (!empty($input['game_state'])) {
$newState = json_decode($input['game_state'], true) ?: [];
$sdb = supabaseService();
$matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'game_state', 'limit' => 1]);
$existing = [];
if (is_array($matches) && !empty($matches) && !isset($matches['error'])) {
......@@ -118,7 +121,7 @@ function handleGameMove($db, string $userId, array $input): void {
$update['game_state'] = json_encode($merged);
}
$result = $db->update('matches', $update, ['id' => 'eq.' . $matchId]);
$result = $sdb->update('matches', $update, ['id' => 'eq.' . $matchId]);
if (isset($result['error'])) jsonError($result['error']);
jsonResponse(['success' => true]);
......@@ -128,13 +131,16 @@ function handleResign($db, string $userId, array $input): void {
$matchId = $input['match_id'] ?? '';
if (!$matchId) jsonError('match_id required');
$match = $db->getOne('matches', ['id' => 'eq.' . $matchId]);
$sdb = supabaseService();
$matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'id,white_player_id,black_player_id,status', 'limit' => 1]);
$match = (is_array($matches) && !empty($matches) && !isset($matches['error'])) ? $matches[0] : null;
if (!$match) jsonError('Match not found', 404);
if ($match['status'] === 'completed') jsonError('Match already ended');
$isWhite = $match['white_player_id'] === $userId;
$result = $isWhite ? 'black_wins' : 'white_wins';
$db->update('matches', [
$sdb->update('matches', [
'status' => 'completed',
'result' => $result,
'ended_at' => date('c')
......@@ -143,6 +149,26 @@ function handleResign($db, string $userId, array $input): void {
jsonResponse(['result' => $result]);
}
function handleDraw($db, string $userId, array $input): void {
$matchId = $input['match_id'] ?? '';
if (!$matchId) jsonError('match_id required');
$sdb = supabaseService();
$matches = $sdb->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'id,status', 'limit' => 1]);
$match = (is_array($matches) && !empty($matches) && !isset($matches['error'])) ? $matches[0] : null;
if (!$match) jsonError('Match not found', 404);
if ($match['status'] === 'completed') jsonError('Match already ended');
$sdb->update('matches', [
'status' => 'completed',
'result' => 'draw',
'ended_at' => date('c'),
'game_state' => json_encode(['draw_accepted' => true])
], ['id' => 'eq.' . $matchId]);
jsonResponse(['result' => 'draw']);
}
function handleComplete($db, string $userId, array $input): void {
$matchId = $input['match_id'] ?? '';
$result = $input['result'] ?? '';
......@@ -155,7 +181,8 @@ function handleComplete($db, string $userId, array $input): void {
jsonError('match_id and result are required');
}
$db->update('matches', [
$sdb = supabaseService();
$sdb->update('matches', [
'status' => 'completed',
'result' => $result,
'current_fen' => $fen,
......@@ -165,7 +192,7 @@ function handleComplete($db, string $userId, array $input): void {
// Calculate Elo rating change
$ratingCol = getRatingColumn($timeControl);
$profiles = $db->get('profiles', ['id' => 'eq.' . $userId, 'select' => $ratingCol . ',games_played,total_wins,total_draws,total_losses,win_streak,coins,xp,level', 'limit' => 1]);
$profiles = $sdb->get('profiles', ['id' => 'eq.' . $userId, 'select' => $ratingCol . ',games_played,total_wins,total_draws,total_losses,win_streak,coins,xp,level', 'limit' => 1]);
$profile = is_array($profiles) && !empty($profiles) ? $profiles[0] : null;
if ($profile) {
......@@ -186,10 +213,9 @@ function handleComplete($db, string $userId, array $input): void {
$updates['win_streak'] = 0;
}
$db->update('profiles', $updates, ['id' => 'eq.' . $userId]);
$sdb->update('profiles', $updates, ['id' => 'eq.' . $userId]);
// Record rating history
$sdb = supabaseService();
$sdb->insert('rating_history', [
'player_id' => $userId,
'game_key' => 'chess',
......
......@@ -21,12 +21,12 @@ export function start(matchId, gameType, options = {}) {
ui.showConnectionRestored();
}
// Detect new move
// Always forward data so game can process resign/draw/state changes
const moveCount = data.move_count || data.current_turn || 0;
if (moveCount > lastMoveCount) {
lastMoveCount = moveCount;
onMove?.(data);
}
onMove?.(data);
// Detect game ended externally
if (data.status === 'completed') {
......
......@@ -815,9 +815,8 @@ function checkDrawOffer(el, rawGameState, myId) {
dialog.querySelector('#draw-accept').addEventListener('click', () => {
dialog.remove();
net.post('game.php', {
action: 'move',
match_id: gameState.matchId,
game_state: JSON.stringify({ draw_accepted: true, draw_offer: null, draw_offer_t: null })
action: 'draw',
match_id: gameState.matchId
});
endGame('draw', 'agreement');
});
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment