Gameplay: Nochmal-Synchronisation + Echtzeit-Commit-Zähler + Host-Button umbenannt
All checks were successful
Pin to IPFS / pin (push) Successful in 11s
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:
parent
a7933315f3
commit
fae9393e8c
3 changed files with 52 additions and 34 deletions
62
app.js
62
app.js
|
|
@ -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 ─── */
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
22
protocol.js
22
protocol.js
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue