Gameplay: Nochmal-Synchronisation + Echtzeit-Commit-Zähler + Host-Button umbenannt
All checks were successful
Pin to IPFS / pin (push) Successful in 11s

- Host-Lobby: 'Münzwurf starten' → 'Spiel starten'
- 'Nochmal': warten bis alle bereit (ready_next/ready_next_all), erst
  dann 'Münzwurf starten' anzeigen
- Progress zeigt Commits/Reveals in Echtzeit (auch vor eigenem Start)
- totalPeers bereits im Konstruktor setzen für sofortige Anzeige
This commit is contained in:
Ole 2026-06-14 22:40:15 +02:00
parent a7933315f3
commit fae9393e8c
3 changed files with 52 additions and 34 deletions

62
app.js
View file

@ -5,6 +5,7 @@
let protocol = null;
let role = null;
let scanningHost = false;
let _nextReady = new Set();
const $ = id => document.getElementById(id);
@ -232,14 +233,26 @@
});
}
/* ─── Show start-protocol button after all ready ─── */
function _showStartButton() {
$('btn-start-protocol').hidden = false;
$('result-area').hidden = true;
$('results-container').hidden = true;
$('protocol-text').textContent = 'Bereit zum Münzwurf';
$('btn-retry').disabled = false;
$('btn-retry').textContent = 'Nochmal';
updatePhases('IDLE');
}
/* ─── Game setup ─── */
function setupGame(autostart) {
function setupGame() {
_nextReady = new Set();
show('game');
renderPlayers('game-player-list');
$('btn-start-protocol').hidden = autostart || role !== 'host';
$('btn-start-protocol').hidden = false;
$('results-container').hidden = true;
$('result-area').hidden = true;
$('protocol-text').textContent = autostart ? 'Starte Protokoll...' : 'Bereit zum Münzwurf';
$('protocol-text').textContent = 'Bereit zum Münzwurf';
updatePhases('IDLE');
protocol = new CoinFlipProtocol(mesh);
@ -264,23 +277,23 @@
};
mesh.ondata = (peerId, msg) => {
if (msg.type === 'protocol_start') {
if (protocol) protocol.reset();
$('results-container').hidden = true;
$('result-area').hidden = true;
$('btn-start-protocol').hidden = true;
updatePhases('IDLE');
protocol.start();
if (msg.type === 'ready_next') {
_nextReady.add(peerId);
const all = [mesh.myPeerId, ...mesh.getPeerIds()];
if (_nextReady.size >= all.length) {
mesh.broadcast({ type: 'ready_next_all' });
_showStartButton();
}
return;
}
if (msg.type === 'ready_next_all') {
_showStartButton();
return;
}
if (msg.type === 'protocol' && protocol) {
protocol.handleMessage(peerId, msg);
}
};
if (autostart) {
protocol.start();
}
}
/* ─── Start screen ─── */
@ -392,8 +405,8 @@
};
mesh.ondata = (peerId, msg) => {
if (msg.type === 'protocol_start') {
setupGame(true);
if (msg.type === 'game_start') {
setupGame();
}
};
@ -426,13 +439,15 @@
/* ─── Host: start game button (in host screen) ─── */
$('btn-start-game').addEventListener('click', () => {
setupGame(false);
if (!mesh) return;
mesh.broadcast({ type: 'game_start' });
setupGame();
});
/* ─── Host: start protocol button (in game screen) ─── */
$('btn-start-protocol').addEventListener('click', async () => {
if (!mesh || !protocol) return;
mesh.broadcast({ type: 'protocol_start' });
_nextReady = new Set();
$('btn-start-protocol').hidden = true;
await protocol.start();
});
@ -440,11 +455,10 @@
/* ─── Retry / again ─── */
$('btn-retry').addEventListener('click', () => {
if (protocol) protocol.reset();
$('results-container').hidden = true;
$('result-area').hidden = true;
$('btn-start-protocol').hidden = role !== 'host';
$('protocol-text').textContent = 'Bereit zum Münzwurf';
updatePhases('IDLE');
_nextReady.add(mesh.myPeerId);
mesh.broadcast({ type: 'ready_next' });
$('btn-retry').disabled = true;
$('btn-retry').textContent = 'Warte...';
});
/* ─── Result screen → play again ─── */
@ -452,7 +466,7 @@
if (protocol) protocol.reset();
$('results-container').hidden = true;
$('result-area').hidden = true;
setupGame(false);
setupGame();
});
/* ─── Back to start ─── */

View file

@ -50,7 +50,7 @@
<button id="btn-host-answer-paste" class="btn-small">Antwort einspielen</button>
</details>
</div>
<button id="btn-start-game" class="btn-primary" disabled>Münzwurf starten</button>
<button id="btn-start-game" class="btn-primary" disabled>Spiel starten</button>
</div>
</section>

View file

@ -6,7 +6,7 @@ class CoinFlipProtocol {
this.commits = new Map();
this.reveals = new Map();
this.result = null;
this.totalPeers = 0;
this.totalPeers = 1 + mesh.getPeerIds().length;
this.expectedPeers = new Set();
this.TIMEOUT_MS = 12000;
this._commitTimer = null;
@ -26,7 +26,7 @@ class CoinFlipProtocol {
this.secret = await generateSecret();
const myCommit = await commit(this.secret);
this.commits.set(this.mesh.myPeerId, myCommit);
if (this.onprogress) this.onprogress('commit', 1, this.totalPeers);
if (this.onprogress) this.onprogress('commit', this.commits.size, this.totalPeers);
if (this.onstatechange) this.onstatechange('COMMITTING');
this.mesh.broadcast({ type: 'protocol', phase: 'commit', data: bufToBase64(myCommit) });
this.state = 'WAITING_FOR_COMMITS';
@ -38,20 +38,24 @@ class CoinFlipProtocol {
handleMessage(peerId, msg) {
if (msg.type !== 'protocol') return;
if (msg.phase === 'commit' && (this.state === 'WAITING_FOR_COMMITS' || this.state === 'COMMITTING' || this.state === 'STARTING')) {
if (msg.phase === 'commit') {
if (!this.commits.has(peerId)) {
this.commits.set(peerId, base64ToBuf(msg.data));
if (this.onprogress) this.onprogress('commit', this.commits.size, this.totalPeers);
this._checkCommits();
if (this.onprogress)
this.onprogress('commit', this.commits.size, this.totalPeers);
if (this.state === 'WAITING_FOR_COMMITS') this._checkCommits();
}
return;
}
if (msg.phase === 'reveal' && (this.state === 'WAITING_FOR_REVEALS' || this.state === 'REVEALING')) {
if (msg.phase === 'reveal') {
if (!this.reveals.has(peerId)) {
this.reveals.set(peerId, base64ToBuf(msg.data));
if (this.onprogress) this.onprogress('reveal', this.reveals.size, this.totalPeers);
this._checkReveals();
if (this.onprogress)
this.onprogress('reveal', this.reveals.size, this.totalPeers);
if (this.state === 'WAITING_FOR_REVEALS') this._checkReveals();
}
return;
}
}
@ -112,7 +116,7 @@ class CoinFlipProtocol {
if (this.onstatechange) this.onstatechange('REVEALING');
this.mesh.broadcast({ type: 'protocol', phase: 'reveal', data: bufToBase64(this.secret) });
this.reveals.set(this.mesh.myPeerId, this.secret);
if (this.onprogress) this.onprogress('reveal', 1, this.totalPeers);
if (this.onprogress) this.onprogress('reveal', this.reveals.size, this.totalPeers);
this.state = 'WAITING_FOR_REVEALS';
if (this.onstatechange) this.onstatechange('WAITING_FOR_REVEALS');
this._startRevealTimer();