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.
116 lines
4.7 KiB
Twig
116 lines
4.7 KiB
Twig
{% extends 'layout/site.html.twig' %}
|
|
|
|
{% block title %}Waiting for players - {{ session.game.name }}{% endblock %}
|
|
|
|
{% block body %}
|
|
<div class="row justify-content-center">
|
|
<div class="col-md-8">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header bg-primary text-white">
|
|
<h3 class="card-title mb-0">Waiting for all players to be ready</h3>
|
|
</div>
|
|
<div class="card-body">
|
|
<h4>{{ session.game.name }}</h4>
|
|
<p>Welcome to the game! Please wait for all players to join and signal they are ready to start.</p>
|
|
|
|
<div class="game-info">
|
|
Please keep the following things in mind:
|
|
<ul>
|
|
<li>This game is best played in full screen mode. For windows, press F11. For Mac, press Cmd+Ctrl+F.</li>
|
|
<li>There is no need to reload the page. There is even a chance this could break the game.</li>
|
|
<li>If your internet connection is lost, you can get back in the game after internet has been fixed.</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="alert alert-info">
|
|
<strong>Game Information:</strong>
|
|
<ul class="mb-0">
|
|
<li>Number of players: {{ session.game.numberOfPlayers }}</li>
|
|
<li>Current players: {{ session.players|length }} / {{ session.game.numberOfPlayers }}</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<h5>Players status:</h5>
|
|
<ul class="list-group">
|
|
{% for player in session.players %}
|
|
<li class="list-group-item d-flex justify-content-between align-items-center">
|
|
{{ player.user.username }}
|
|
{% set playerReady = false %}
|
|
{% set settingName = 'ReadyAtForPlayer' ~ player.screen %}
|
|
{% for setting in session.settings %}
|
|
{% if setting.name.value == settingName and setting.player == player %}
|
|
{% set playerReady = true %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if playerReady %}
|
|
<span class="badge bg-success">Ready</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">Not ready</span>
|
|
{% endif %}
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<form method="post" class="mt-4">
|
|
<div class="form-check form-switch mb-3">
|
|
<input class="form-check-input" type="checkbox" id="toggle_ready" name="toggle_ready" onchange="this.form.submit()" {{ isReady ? 'checked' : '' }}>
|
|
<label class="form-check-label" for="toggle_ready">
|
|
<strong>I am ready to start!</strong>
|
|
</label>
|
|
</div>
|
|
<p class="text-muted small">
|
|
As soon as everyone has checked this box, the game starts automatically for everyone.
|
|
</p>
|
|
</form>
|
|
|
|
<div class="mt-3">
|
|
<a href="{{ path('game_dashboard') }}" class="btn btn-outline-secondary">Back to Dashboard</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="mercure-config"
|
|
data-mercure-public-url="{{ mercure_public_url|e('html_attr') }}"
|
|
data-topic="/game/hub/{{ session.id|e('html_attr') }}"
|
|
style="display:none">
|
|
</div>
|
|
|
|
<script>
|
|
const config = document.getElementById('mercure-config');
|
|
const publicUrl = config.dataset.mercurePublicUrl;
|
|
const topic = config.dataset.topic;
|
|
|
|
let reloading = false;
|
|
const reloadOnce = (eventSource) => {
|
|
if (reloading) {
|
|
return;
|
|
}
|
|
reloading = true;
|
|
if (eventSource) {
|
|
eventSource.close();
|
|
}
|
|
window.location.reload();
|
|
};
|
|
|
|
if (publicUrl && topic) {
|
|
const url = new URL(publicUrl);
|
|
url.searchParams.append('topic', topic);
|
|
|
|
const eventSource = new EventSource(url);
|
|
eventSource.onmessage = event => {
|
|
const data = JSON.parse(event.data);
|
|
if (data.type === 'all_ready' || data.type === 'player_ready') {
|
|
reloadOnce(eventSource);
|
|
}
|
|
};
|
|
}
|
|
</script>
|
|
{% endblock %}
|