Commit 6e27ece1 authored by Mahmoud Aglan's avatar Mahmoud Aglan

fix: join_room race condition + My Games for all game types (#32, #61)

- 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: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 84398f94
...@@ -67,7 +67,19 @@ switch ($action) { ...@@ -67,7 +67,19 @@ switch ($action) {
$players[] = $userId; $players[] = $userId;
$newStatus = count($players) >= $maxPlayers ? 'in_progress' : 'waiting'; $newStatus = count($players) >= $maxPlayers ? 'in_progress' : 'waiting';
$sdb->update($table, ['players' => $players, 'status' => $newStatus], ['id' => 'eq.' . $match['id']]); $sdb->update($table, ['players' => $players, 'status' => $newStatus], [
'id' => 'eq.' . $match['id'],
'status' => 'eq.waiting'
]);
// Verify join succeeded (re-read to detect race)
$verify = $sdb->get($table, ['id' => 'eq.' . $match['id'], 'select' => 'players', 'limit' => 1]);
if (is_array($verify) && !empty($verify) && !isset($verify['error'])) {
$finalPlayers = is_array($verify[0]['players']) ? $verify[0]['players'] : json_decode($verify[0]['players'] ?? '[]', true);
if (!in_array($userId, $finalPlayers)) {
jsonError('Room is full', 409);
}
}
jsonResponse(['success' => true, 'match_id' => $match['id']]); jsonResponse(['success' => true, 'match_id' => $match['id']]);
break; break;
......
...@@ -173,6 +173,7 @@ const strings = { ...@@ -173,6 +173,7 @@ const strings = {
'game.bot_thinking': '{name} يفكر...', 'game.bot_thinking': '{name} يفكر...',
'game.rating': 'التصنيف', 'game.rating': 'التصنيف',
'game.coins': 'عملات', 'game.coins': 'عملات',
'game.my_games': 'مبارياتي',
'game.xp': 'خبرة', 'game.xp': 'خبرة',
'game.moves_count': '{n} نقلة', 'game.moves_count': '{n} نقلة',
'game.rounds_count': '{n} جولة', 'game.rounds_count': '{n} جولة',
...@@ -860,6 +861,7 @@ const strings = { ...@@ -860,6 +861,7 @@ const strings = {
'game.bot_thinking': '{name} thinking...', 'game.bot_thinking': '{name} thinking...',
'game.rating': 'Rating', 'game.rating': 'Rating',
'game.coins': 'Coins', 'game.coins': 'Coins',
'game.my_games': 'My Games',
'game.xp': 'XP', 'game.xp': 'XP',
'game.moves_count': '{n} moves', 'game.moves_count': '{n} moves',
'game.rounds_count': '{n} round', 'game.rounds_count': '{n} round',
......
...@@ -301,11 +301,11 @@ function showGameMenu(menu, game) { ...@@ -301,11 +301,11 @@ function showGameMenu(menu, game) {
<span style="font-size:var(--gm-chip-icon-font);">${emoji('tournament_cup', '🏆', 18)}</span> <span style="font-size:var(--gm-chip-icon-font);">${emoji('tournament_cup', '🏆', 18)}</span>
<span>Leaderboard</span> <span>Leaderboard</span>
</button> </button>
${game.key === 'chess' ? `<button class="gm-chip" id="btn-history"> <button class="gm-chip" id="btn-history">
<span style="font-size:var(--gm-chip-icon-font);">${emoji('clipboard', '📋', 18)}</span> <span style="font-size:var(--gm-chip-icon-font);">${emoji('clipboard', '📋', 18)}</span>
<span>My Games</span> <span>${t('game.my_games')}</span>
</button> </button>
<button class="gm-chip" id="btn-puzzles"> ${game.key === 'chess' ? `<button class="gm-chip" id="btn-puzzles">
<span style="font-size:var(--gm-chip-icon-font);">${emoji('puzzle', '🧩', 18)}</span> <span style="font-size:var(--gm-chip-icon-font);">${emoji('puzzle', '🧩', 18)}</span>
<span>Puzzles</span> <span>Puzzles</span>
</button>` : ''} </button>` : ''}
...@@ -380,7 +380,7 @@ function showGameMenu(menu, game) { ...@@ -380,7 +380,7 @@ function showGameMenu(menu, game) {
menu.querySelector('#btn-history')?.addEventListener('click', () => { menu.querySelector('#btn-history')?.addEventListener('click', () => {
audio.play('click'); audio.play('click');
menu.classList.add('hidden'); menu.classList.add('hidden');
scene.push('chess-history', { game: game.key }); scene.push('chess-history', { game: game.key, gameKey: game.key });
}); });
menu.querySelector('#btn-puzzles')?.addEventListener('click', () => { menu.querySelector('#btn-puzzles')?.addEventListener('click', () => {
......
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