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 protocol = null;
|
||||||
let role = null;
|
let role = null;
|
||||||
let scanningHost = false;
|
let scanningHost = false;
|
||||||
|
let _nextReady = new Set();
|
||||||
|
|
||||||
const $ = id => document.getElementById(id);
|
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 ─── */
|
/* ─── Game setup ─── */
|
||||||
function setupGame(autostart) {
|
function setupGame() {
|
||||||
|
_nextReady = new Set();
|
||||||
show('game');
|
show('game');
|
||||||
renderPlayers('game-player-list');
|
renderPlayers('game-player-list');
|
||||||
$('btn-start-protocol').hidden = autostart || role !== 'host';
|
$('btn-start-protocol').hidden = false;
|
||||||
$('results-container').hidden = true;
|
$('results-container').hidden = true;
|
||||||
$('result-area').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');
|
updatePhases('IDLE');
|
||||||
|
|
||||||
protocol = new CoinFlipProtocol(mesh);
|
protocol = new CoinFlipProtocol(mesh);
|
||||||
|
|
@ -264,23 +277,23 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
mesh.ondata = (peerId, msg) => {
|
mesh.ondata = (peerId, msg) => {
|
||||||
if (msg.type === 'protocol_start') {
|
if (msg.type === 'ready_next') {
|
||||||
if (protocol) protocol.reset();
|
_nextReady.add(peerId);
|
||||||
$('results-container').hidden = true;
|
const all = [mesh.myPeerId, ...mesh.getPeerIds()];
|
||||||
$('result-area').hidden = true;
|
if (_nextReady.size >= all.length) {
|
||||||
$('btn-start-protocol').hidden = true;
|
mesh.broadcast({ type: 'ready_next_all' });
|
||||||
updatePhases('IDLE');
|
_showStartButton();
|
||||||
protocol.start();
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (msg.type === 'ready_next_all') {
|
||||||
|
_showStartButton();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type === 'protocol' && protocol) {
|
if (msg.type === 'protocol' && protocol) {
|
||||||
protocol.handleMessage(peerId, msg);
|
protocol.handleMessage(peerId, msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (autostart) {
|
|
||||||
protocol.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Start screen ─── */
|
/* ─── Start screen ─── */
|
||||||
|
|
@ -392,8 +405,8 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
mesh.ondata = (peerId, msg) => {
|
mesh.ondata = (peerId, msg) => {
|
||||||
if (msg.type === 'protocol_start') {
|
if (msg.type === 'game_start') {
|
||||||
setupGame(true);
|
setupGame();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -426,13 +439,15 @@
|
||||||
|
|
||||||
/* ─── Host: start game button (in host screen) ─── */
|
/* ─── Host: start game button (in host screen) ─── */
|
||||||
$('btn-start-game').addEventListener('click', () => {
|
$('btn-start-game').addEventListener('click', () => {
|
||||||
setupGame(false);
|
if (!mesh) return;
|
||||||
|
mesh.broadcast({ type: 'game_start' });
|
||||||
|
setupGame();
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ─── Host: start protocol button (in game screen) ─── */
|
/* ─── Host: start protocol button (in game screen) ─── */
|
||||||
$('btn-start-protocol').addEventListener('click', async () => {
|
$('btn-start-protocol').addEventListener('click', async () => {
|
||||||
if (!mesh || !protocol) return;
|
if (!mesh || !protocol) return;
|
||||||
mesh.broadcast({ type: 'protocol_start' });
|
_nextReady = new Set();
|
||||||
$('btn-start-protocol').hidden = true;
|
$('btn-start-protocol').hidden = true;
|
||||||
await protocol.start();
|
await protocol.start();
|
||||||
});
|
});
|
||||||
|
|
@ -440,11 +455,10 @@
|
||||||
/* ─── Retry / again ─── */
|
/* ─── Retry / again ─── */
|
||||||
$('btn-retry').addEventListener('click', () => {
|
$('btn-retry').addEventListener('click', () => {
|
||||||
if (protocol) protocol.reset();
|
if (protocol) protocol.reset();
|
||||||
$('results-container').hidden = true;
|
_nextReady.add(mesh.myPeerId);
|
||||||
$('result-area').hidden = true;
|
mesh.broadcast({ type: 'ready_next' });
|
||||||
$('btn-start-protocol').hidden = role !== 'host';
|
$('btn-retry').disabled = true;
|
||||||
$('protocol-text').textContent = 'Bereit zum Münzwurf';
|
$('btn-retry').textContent = 'Warte...';
|
||||||
updatePhases('IDLE');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ─── Result screen → play again ─── */
|
/* ─── Result screen → play again ─── */
|
||||||
|
|
@ -452,7 +466,7 @@
|
||||||
if (protocol) protocol.reset();
|
if (protocol) protocol.reset();
|
||||||
$('results-container').hidden = true;
|
$('results-container').hidden = true;
|
||||||
$('result-area').hidden = true;
|
$('result-area').hidden = true;
|
||||||
setupGame(false);
|
setupGame();
|
||||||
});
|
});
|
||||||
|
|
||||||
/* ─── Back to start ─── */
|
/* ─── Back to start ─── */
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
<button id="btn-host-answer-paste" class="btn-small">Antwort einspielen</button>
|
<button id="btn-host-answer-paste" class="btn-small">Antwort einspielen</button>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</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>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
22
protocol.js
22
protocol.js
|
|
@ -6,7 +6,7 @@ class CoinFlipProtocol {
|
||||||
this.commits = new Map();
|
this.commits = new Map();
|
||||||
this.reveals = new Map();
|
this.reveals = new Map();
|
||||||
this.result = null;
|
this.result = null;
|
||||||
this.totalPeers = 0;
|
this.totalPeers = 1 + mesh.getPeerIds().length;
|
||||||
this.expectedPeers = new Set();
|
this.expectedPeers = new Set();
|
||||||
this.TIMEOUT_MS = 12000;
|
this.TIMEOUT_MS = 12000;
|
||||||
this._commitTimer = null;
|
this._commitTimer = null;
|
||||||
|
|
@ -26,7 +26,7 @@ class CoinFlipProtocol {
|
||||||
this.secret = await generateSecret();
|
this.secret = await generateSecret();
|
||||||
const myCommit = await commit(this.secret);
|
const myCommit = await commit(this.secret);
|
||||||
this.commits.set(this.mesh.myPeerId, myCommit);
|
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');
|
if (this.onstatechange) this.onstatechange('COMMITTING');
|
||||||
this.mesh.broadcast({ type: 'protocol', phase: 'commit', data: bufToBase64(myCommit) });
|
this.mesh.broadcast({ type: 'protocol', phase: 'commit', data: bufToBase64(myCommit) });
|
||||||
this.state = 'WAITING_FOR_COMMITS';
|
this.state = 'WAITING_FOR_COMMITS';
|
||||||
|
|
@ -38,20 +38,24 @@ class CoinFlipProtocol {
|
||||||
handleMessage(peerId, msg) {
|
handleMessage(peerId, msg) {
|
||||||
if (msg.type !== 'protocol') return;
|
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)) {
|
if (!this.commits.has(peerId)) {
|
||||||
this.commits.set(peerId, base64ToBuf(msg.data));
|
this.commits.set(peerId, base64ToBuf(msg.data));
|
||||||
if (this.onprogress) this.onprogress('commit', this.commits.size, this.totalPeers);
|
if (this.onprogress)
|
||||||
this._checkCommits();
|
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)) {
|
if (!this.reveals.has(peerId)) {
|
||||||
this.reveals.set(peerId, base64ToBuf(msg.data));
|
this.reveals.set(peerId, base64ToBuf(msg.data));
|
||||||
if (this.onprogress) this.onprogress('reveal', this.reveals.size, this.totalPeers);
|
if (this.onprogress)
|
||||||
this._checkReveals();
|
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');
|
if (this.onstatechange) this.onstatechange('REVEALING');
|
||||||
this.mesh.broadcast({ type: 'protocol', phase: 'reveal', data: bufToBase64(this.secret) });
|
this.mesh.broadcast({ type: 'protocol', phase: 'reveal', data: bufToBase64(this.secret) });
|
||||||
this.reveals.set(this.mesh.myPeerId, 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';
|
this.state = 'WAITING_FOR_REVEALS';
|
||||||
if (this.onstatechange) this.onstatechange('WAITING_FOR_REVEALS');
|
if (this.onstatechange) this.onstatechange('WAITING_FOR_REVEALS');
|
||||||
this._startRevealTimer();
|
this._startRevealTimer();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue