From cb2e94541937440790236fae7f468530b4eaf2f8 Mon Sep 17 00:00:00 2001 From: Frank Date: Sat, 11 Jul 2026 21:48:32 +0200 Subject: [PATCH] Remove 60-second ready-status expiry so the group can always start Ready status previously expired 60 seconds after a player checked the box, evaluated per-player against their own timestamp. Unless every player happened to click ready within the same 60-second window, the earliest player's readiness would silently expire before the last one joined, so the session could get stuck on "Waiting for all players to be ready" indefinitely, especially after a reload re-triggered the timeout check. Ready state is now durable: once checked, it stays until the player unchecks it or the whole group is simultaneously ready, at which point the session always transitions to PLAYING regardless of how long that took. session.timer is still only ever set once during that one-way READY -> PLAYING transition, so a reload never restarts or desyncs the countdown between players. --- src/Game/Controller/GameController.php | 9 ++------ src/Game/Service/GameDashboardService.php | 25 ++--------------------- templates/game/waiting.html.twig | 20 +----------------- 3 files changed, 5 insertions(+), 49 deletions(-) diff --git a/src/Game/Controller/GameController.php b/src/Game/Controller/GameController.php index 5c845d6..a285313 100644 --- a/src/Game/Controller/GameController.php +++ b/src/Game/Controller/GameController.php @@ -131,27 +131,22 @@ final class GameController extends AbstractController return $this->redirectToRoute('game', ['session' => $session->getId()]); } - // Periodically check readiness timeout + // Lazily pick up readiness changes from other players since our last request $dashboardService->checkAllPlayersReady($session); if ($session->getStatus() === SessionStatus::READY) { $isReady = false; - $readyAt = null; if ($player) { $settingName = SessionSettingType::tryFrom('ReadyAtForPlayer' . $player->getScreen()); if ($settingName) { $setting = $session->getSettings()->filter(fn(SessionSetting $s) => $s->getName() === $settingName && $s->getPlayer() === $player)->first(); - if ($setting) { - $isReady = true; - $readyAt = (int)$setting->getValue(); - } + $isReady = $setting !== null; } } return $this->render('game/waiting.html.twig', [ 'session' => $session, 'isReady' => $isReady, - 'readyAt' => $readyAt, 'mercure_public_url' => $this->mercurePublicUrl, ]); } diff --git a/src/Game/Service/GameDashboardService.php b/src/Game/Service/GameDashboardService.php index 3a9c4be..9f91c47 100644 --- a/src/Game/Service/GameDashboardService.php +++ b/src/Game/Service/GameDashboardService.php @@ -355,8 +355,6 @@ final class GameDashboardService } $readyPlayersCount = 0; - $now = new \DateTime(); - $anyReset = false; /** @var \App\Game\Repository\SessionSettingRepository $settingRepo */ $settingRepo = $this->entityManager->getRepository(SessionSetting::class); @@ -367,27 +365,8 @@ final class GameDashboardService continue; } - $setting = $settingRepo->getSetting($session, $settingName, $player); - if ($setting) { - $readyAtTimestamp = (int)$setting->getValue(); - // Check timeout: 1 minute = 60 seconds - if (($now->getTimestamp() - $readyAtTimestamp) > 60) { - $session->removeSetting($setting); - $this->entityManager->remove($setting); - $anyReset = true; - } else { - $readyPlayersCount++; - } - } - } - - if ($anyReset) { - $this->entityManager->flush(); - try { - $topic = '/game/hub/' . $session->getId(); - $this->hub->publish(new Update($topic, json_encode(['type' => 'player_ready']))); - } catch (\Exception $e) { - // Mercure might be down + if ($settingRepo->getSetting($session, $settingName, $player)) { + $readyPlayersCount++; } } diff --git a/templates/game/waiting.html.twig b/templates/game/waiting.html.twig index 0451d98..fb41edd 100644 --- a/templates/game/waiting.html.twig +++ b/templates/game/waiting.html.twig @@ -64,7 +64,7 @@

- Note: If all players are not ready within 1 minute of you checking this, your status will be reset automatically. + As soon as everyone has checked this box, the game starts automatically for everyone.

@@ -79,7 +79,6 @@ @@ -87,7 +86,6 @@ const config = document.getElementById('mercure-config'); const publicUrl = config.dataset.mercurePublicUrl; const topic = config.dataset.topic; - const readyAt = config.dataset.readyAt; let reloading = false; const reloadOnce = (eventSource) => { @@ -113,21 +111,5 @@ } }; } - - // Client-side timeout for ready status - if (readyAt) { - const timeoutMs = 61000; // 61 seconds (slightly more than server-side 60s) - const now = Date.now(); - const readyAtMs = readyAt * 1000; - const timeElapsed = now - readyAtMs; - const timeLeft = timeoutMs - timeElapsed; - - if (timeLeft > 0) { - setTimeout(() => reloadOnce(), timeLeft); - } else { - // Already timed out, reload to sync with server - reloadOnce(); - } - } {% endblock %}