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.
When the last player clicked ready, toggleReady() published a
redundant 'player_ready' event on top of checkAllPlayersReady()'s
'all_ready', and the client reloaded on every message with no guard
and without closing the EventSource. Chrome kept the old page's
script (and its EventSource) alive across the overlapping reload
calls, causing repeated reconnects to the Mercure hub; Firefox
apparently tore the page down fast enough to mask it. Skip the
redundant publish server-side, and make the client reload idempotent
by tracking whether it already fired and closing the EventSource
before reloading.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>