Room-Code-Logik raus, QR zeigt wieder volles SDP (STUN-frei). Room-Code/Storage/Polling entfernt, QR-Hauptcode immer sichtbar
All checks were successful
Pin to IPFS / pin (push) Successful in 12s

This commit is contained in:
Ole 2026-06-14 15:26:44 +02:00
parent 6c457b506a
commit cef5d35be7
4 changed files with 8 additions and 169 deletions

23
app.js
View file

@ -187,10 +187,6 @@
showQR('qr-host', d);
const raw = $('host-qr-raw');
if (raw) raw.textContent = d;
const codeEl = $('host-room-code');
if (codeEl && mesh.roomCode) codeEl.textContent = mesh.roomCode;
const fullSDP = mesh.getFullSDPData ? mesh.getFullSDPData() : null;
if (fullSDP) showQR('qr-host-full', fullSDP);
};
mesh.onreadyupdate = (ready, total) => {
$('btn-start-game').disabled = ready < total || total === 0;
@ -268,15 +264,10 @@
let scanned = false;
camScan('join-camera', 'join-camera-canvas', (parsed, raw) => {
if (scanned) return;
if (!parsed.id) return;
if (!parsed.s || !parsed.id) return;
scanned = true;
$('join-scan-status').textContent = 'Verbinde...';
if (parsed.room) {
$('join-room-input').value = parsed.room;
mesh.joinFromRoomCode(parsed.room);
} else if (parsed.s) {
mesh.joinFromQR(raw);
}
mesh.joinFromQR(raw);
});
} catch (e) {
$('join-scan-status').textContent = 'Kamera nicht verfügbar';
@ -293,16 +284,6 @@
$('join-scan-status').textContent = 'Verbinde...';
});
$('btn-join-room').addEventListener('click', () => {
const code = $('join-room-input').value.trim().toUpperCase();
if (!code) return;
if (!mesh) { show('start'); return; }
camStop();
$('join-scan-status').textContent = 'Verbinde...';
$('join-camera').classList.remove('active');
mesh.joinFromRoomCode(code);
});
/* ─── Host: start game button (in host screen) ─── */
$('btn-start-game').addEventListener('click', () => {
setupGame(false);

View file

@ -27,11 +27,10 @@
<div class="qr-section">
<div id="qr-host"></div>
<p class="hint">Scannen lassen, um beizutreten</p>
</div>
<div class="room-code debug-only">
<span class="room-code-label">Raum-Code</span>
<span class="room-code-value" id="host-room-code"></span>
<p class="hint">Auf gleichem Rechner: Code in anderem Tab eingeben</p>
<details class="debug-helper debug-only" style="margin-top:8px">
<summary>QR-Rohdaten</summary>
<pre id="host-qr-raw" class="raw-data"></pre>
</details>
</div>
<div id="host-players" class="player-section">
<h3>Spieler</h3>
@ -42,19 +41,11 @@
<video id="host-camera" autoplay playsinline muted></video>
<canvas id="host-camera-canvas" hidden></canvas>
<p id="host-scan-status"></p>
<details class="debug-helper debug-only">
<summary>QR-Rohdaten</summary>
<pre id="host-qr-raw" class="raw-data"></pre>
</details>
<details class="debug-helper debug-only">
<summary>Antwort manuell einfügen</summary>
<textarea id="host-answer-paste" rows="3" placeholder="Antwort-JSON hier einfügen..."></textarea>
<button id="btn-host-answer-paste" class="btn-small">Antwort einspielen</button>
</details>
<details class="debug-helper debug-only">
<summary>Full-SDP-QR (für andere Geräte)</summary>
<div id="qr-host-full"></div>
</details>
</div>
<button id="btn-start-game" class="btn-primary" disabled>Münzwurf starten</button>
</div>
@ -68,10 +59,6 @@
<video id="join-camera" autoplay playsinline muted></video>
<canvas id="join-camera-canvas" hidden></canvas>
<p id="join-scan-status">QR-Code scannen...</p>
<div class="debug-only" style="margin-top:12px">
<input id="join-room-input" type="text" placeholder="Raum-Code" maxlength="4" style="width:100px;padding:10px;text-align:center;font-size:1.2rem;font-family:monospace;text-transform:uppercase;background:var(--bg);border:1px solid var(--border);border-radius:6px;color:var(--text)">
<button id="btn-join-room" class="btn-small" style="margin-top:8px">Beitreten</button>
</div>
<details class="debug-helper debug-only">
<summary>QR manuell einfügen</summary>
<textarea id="join-qr-paste" rows="3" placeholder="QR-JSON hier einfügen..."></textarea>

108
p2p.js
View file

@ -11,9 +11,6 @@ class MeshNet {
this.roster = [];
this.readyPeers = new Set();
this._meshReadySent = false;
this.roomCode = null;
this._processedAnswers = new Set();
this._answerPollInterval = null;
this.onpeerconnect = null;
this.onpeerdisconnect = null;
@ -23,59 +20,11 @@ class MeshNet {
this.onreadyupdate = null;
}
_generateRoomCode() {
return Math.random().toString(36).substring(2, 6).toUpperCase();
}
_startAnswerPolling() {
this._stopAnswerPolling();
this._answerPollInterval = setInterval(() => {
if (!this.isHost || !this.roomCode || !this._pendingPeer || this._pendingPeer.signalReceived) return;
const prefix = `rp2p_answer_${this.roomCode}_`;
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(prefix) && !this._processedAnswers.has(key)) {
this._processedAnswers.add(key);
try {
const raw = localStorage.getItem(key);
if (!raw) continue;
const data = JSON.parse(raw);
this.feedAnswer(JSON.stringify({ v: 1, id: data.id, s: data.s }));
} catch (err) {
console.error('poll answer error:', err);
}
}
}
}, 400);
}
_stopAnswerPolling() {
if (this._answerPollInterval) {
clearInterval(this._answerPollInterval);
this._answerPollInterval = null;
}
}
/* ─── Host: room ─── */
createRoom() {
this.isHost = true;
this.roomCode = this._generateRoomCode();
this._processedAnswers = new Set();
this._cleanupStorage();
this._createPendingPeer();
this._startAnswerPolling();
}
_cleanupStorage() {
const toRemove = [];
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && (key.startsWith('rp2p_offer_') || key.startsWith('rp2p_answer_'))) {
toRemove.push(key);
}
}
toRemove.forEach(k => localStorage.removeItem(k));
}
_createPendingPeer() {
@ -90,15 +39,8 @@ class MeshNet {
if (signalSent) return;
signalSent = true;
const sdpJson = JSON.stringify({ v: 1, id: this.myPeerId, s: signal });
this._fullSDPData = sdpJson;
if (this.roomCode) {
localStorage.setItem(`rp2p_offer_${this.roomCode}`, sdpJson);
this._currentQRData = JSON.stringify({ v: 1, id: this.myPeerId, room: this.roomCode });
} else {
this._currentQRData = sdpJson;
}
this._currentQRData = JSON.stringify({ v: 1, id: this.myPeerId, s: signal });
this._fullSDPData = this._currentQRData;
if (this.onqrupdate) this.onqrupdate(this._currentQRData);
});
@ -175,51 +117,6 @@ class MeshNet {
this._pendingPeer = { peer, peerId: remoteId, connected: answerSent };
}
joinFromRoomCode(code, _retries = 10) {
const key = `rp2p_offer_${code.toUpperCase()}`;
const offerStr = localStorage.getItem(key);
if (!offerStr) {
if (_retries > 0) {
setTimeout(() => this.joinFromRoomCode(code, _retries - 1), 500);
}
return;
}
const offerData = JSON.parse(offerStr);
const remoteId = offerData.id;
this.hostPeerId = remoteId;
const offer = offerData.s;
const peer = new SimplePeer({ initiator: false, trickle: false, config: { iceServers: [] } });
let answerSent = false;
peer.on('signal', signal => {
if (answerSent) return;
answerSent = true;
const answerKey = `rp2p_answer_${code.toUpperCase()}_${this.myPeerId}`;
localStorage.setItem(answerKey, JSON.stringify({ id: this.myPeerId, s: signal }));
});
peer.on('connect', () => {
this.connections.set(remoteId, peer);
peer.send(JSON.stringify({ type: 'identity', peerId: this.myPeerId }));
if (this.onpeerconnect) this.onpeerconnect(remoteId);
});
peer.on('data', data => {
try {
const msg = JSON.parse(data.toString());
this._handleMessage(msg, peer);
} catch (e) { console.error('p2p data error:', e); }
});
peer.on('close', () => this._cleanupPeer(peer));
peer.on('error', err => console.error('p2p error:', err));
peer.signal(offer);
this.connections.set(remoteId, peer);
this._pendingPeer = { peer, peerId: remoteId, connected: answerSent };
}
/* ─── Mesh helpers ─── */
_createSimplePeer(initiator, onSignal, onConnect) {
@ -451,7 +348,6 @@ class MeshNet {
}
destroy() {
this._stopAnswerPolling();
for (const peer of this.connections.values()) {
try { peer.destroy(); } catch (e) {}
}

View file

@ -382,30 +382,5 @@ h3 {
background: var(--bg-hover);
}
/* Room code (debug mode) */
.room-code {
margin: 16px 0;
text-align: center;
}
.room-code-label {
display: block;
font-size: 0.8rem;
color: var(--text-dim);
margin-bottom: 4px;
}
.room-code-value {
display: inline-block;
font-family: 'Courier New', monospace;
font-size: 2.5rem;
font-weight: 800;
letter-spacing: 6px;
color: var(--gold);
background: var(--bg-card);
border: 2px dashed var(--border);
border-radius: var(--radius);
padding: 8px 24px;
user-select: all;
}
/* Utility */
.hidden { display: none !important; }