From becd1593b6876f64b2a7a11674de44d4485442f1 Mon Sep 17 00:00:00 2001 From: Ole Date: Sun, 14 Jun 2026 18:33:36 +0200 Subject: [PATCH] Pairing-Logs in ?debug: _log() in p2p.js, Panel links unten --- app.js | 15 ++++++++++++++- index.html | 4 ++++ p2p.js | 43 +++++++++++++++++++++++++++++++++++++++---- style.css | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 95 insertions(+), 5 deletions(-) diff --git a/app.js b/app.js index d3868ce..7b1214f 100644 --- a/app.js +++ b/app.js @@ -281,6 +281,7 @@ mesh.playerName = name; mesh.names.set(mesh.myPeerId, name || mesh.myPeerId.slice(0, 8)); mesh.onchat = (peerId, text) => addChatMessage(mesh.getPeerName(peerId), text); + mesh.onlog = addPairingLog; mesh.onpeerconnect = pid => { renderPlayers('host-player-list'); @@ -348,7 +349,7 @@ mesh.playerName = name; mesh.names.set(mesh.myPeerId, name || mesh.myPeerId.slice(0, 8)); mesh.onchat = (peerId, text) => addChatMessage(mesh.getPeerName(peerId), text); - show('join'); + mesh.onlog = addPairingLog; $('join-answer-section').hidden = true; $('join-scan-status').textContent = 'QR-Code scannen...'; @@ -438,6 +439,18 @@ show('start'); }); + /* ─── Debug pairing log ─── */ + const pairingMessages = $('debug-pairing-messages'); + + function addPairingLog(msg) { + if (!pairingMessages) return; + const div = document.createElement('div'); + const t = new Date().toLocaleTimeString('de-DE', { hour12: false }); + div.textContent = `[${t}] ${msg}`; + pairingMessages.appendChild(div); + pairingMessages.scrollTop = pairingMessages.scrollHeight; + } + /* ─── Debug chat ─── */ const chatInput = $('debug-chat-text'); const chatMessages = $('debug-chat-messages'); diff --git a/index.html b/index.html index 13de288..e75712f 100644 --- a/index.html +++ b/index.html @@ -128,6 +128,10 @@ +
+
Pairing-Logs
+
+
diff --git a/p2p.js b/p2p.js index e227da5..1854599 100644 --- a/p2p.js +++ b/p2p.js @@ -22,8 +22,11 @@ class MeshNet { this.onanswerready = null; this.onreadyupdate = null; this.onchat = null; + this.onlog = null; } + _log(msg) { if (this.onlog) this.onlog(msg); } + /* ─── Host: room ─── */ createRoom() { @@ -42,6 +45,7 @@ class MeshNet { peer.on('signal', async signal => { if (signalSent) return; signalSent = true; + this._log('SDP-Signal (Offer) empfangen'); const minSignal = signal.sdp ? { ...signal, sdp: stripSDP(signal.sdp) } : signal; const packed = await pack({ v: 1, id: this.myPeerId, s: minSignal }); @@ -49,10 +53,12 @@ class MeshNet { this._fullSDPData = this._currentQRData; if (this.onqrupdate) this.onqrupdate(this._currentQRData); + this._log('Offer QR erstellt'); }); peer.on('connect', () => { peer.send(JSON.stringify({ type: 'identity', peerId: this.myPeerId, name: this.playerName })); + this._log('DataChannel offen, sende Identity'); }); peer.on('data', data => { @@ -71,14 +77,19 @@ class MeshNet { } async feedAnswer(answerStr) { - if (!this._pendingPeer || this._pendingPeer.signalReceived) return; + if (!this._pendingPeer || this._pendingPeer.signalReceived) { + this._log('feedAnswer ignoriert (bereits verbunden)'); + return; + } try { const data = await unpack(decode45(answerStr)); this._pendingPeer.peerId = data.id; this._pendingPeer.peer.signal(data.s); this._pendingPeer.signalReceived = true; + this._log(`Answer verarbeitet von ${data.id.slice(0, 8)}`); } catch (e) { console.error('feedAnswer error:', e); + this._log(`feedAnswer Fehler: ${e.message}`); } } @@ -89,6 +100,7 @@ class MeshNet { const remoteId = data.id; this.hostPeerId = remoteId; const offer = data.s; + this._log(`QR gelesen, signalisiere Host ${remoteId.slice(0, 8)}`); const peer = new SimplePeer({ initiator: false, trickle: false, config: { iceServers: [] } }); let answerSent = false; @@ -100,11 +112,13 @@ class MeshNet { const packed = await pack({ v: 1, id: this.myPeerId, s: minSignal, to: remoteId }); const answerData = encode45(packed); if (this.onanswerready) this.onanswerready(answerData); + this._log('Answer QR erstellt'); }); peer.on('connect', () => { this.connections.set(remoteId, peer); peer.send(JSON.stringify({ type: 'identity', peerId: this.myPeerId, name: this.playerName })); + this._log(`DataChannel zu Host ${remoteId.slice(0, 8)} offen`); if (this.onpeerconnect) this.onpeerconnect(remoteId); }); @@ -116,7 +130,10 @@ class MeshNet { }); peer.on('close', () => this._cleanupPeer(peer)); - peer.on('error', err => console.error('p2p error:', err)); + peer.on('error', err => { + console.error('p2p error:', err); + this._log(`Peer-Fehler: ${err.message}`); + }); peer.signal(offer); this.connections.set(remoteId, peer); @@ -134,7 +151,7 @@ class MeshNet { peer.on('connect', () => { if (onConnect) onConnect(); - // No identity exchange – peerId known from context + this._log('DataChannel (Mesh) offen'); }); peer.on('data', data => { @@ -145,7 +162,10 @@ class MeshNet { }); peer.on('close', () => this._cleanupPeer(peer)); - peer.on('error', e => console.error('mesh error:', e)); + peer.on('error', e => { + console.error('mesh error:', e); + this._log(`Mesh-Fehler: ${e.message}`); + }); return peer; } @@ -154,6 +174,7 @@ class MeshNet { if (this.connections.has(targetPeerId)) return; if (this._pendingMeshPeers.has(targetPeerId)) return; if (this.myPeerId >= targetPeerId) return; + this._log(`Mesh zu ${targetPeerId.slice(0, 8)} initiiert`); const peer = this._createSimplePeer(true, signal => { @@ -171,6 +192,7 @@ class MeshNet { } _handleRelayedSignal(fromPeerId, signal) { + this._log(`Relayed-Signal von ${fromPeerId.slice(0, 8)}`); let peer = this.connections.get(fromPeerId) || this._pendingMeshPeers.get(fromPeerId); if (!peer) { @@ -192,6 +214,8 @@ class MeshNet { } _checkMeshConnections() { + const missing = this.roster.filter(pid => pid !== this.myPeerId && !this.connections.has(pid) && this.myPeerId < pid); + if (missing.length) this._log(`Prüfe Mesh: ${missing.length} fehlende Verbindungen`); for (const pid of this.roster) { if (pid === this.myPeerId) continue; if (this.connections.has(pid)) continue; @@ -214,6 +238,7 @@ class MeshNet { if (allConnected && this.roster.length > 0) { this._meshReadySent = true; this.sendTo(this.hostPeerId, { type: 'mesh_ready' }); + this._log('Mesh ready gesendet'); } } @@ -221,6 +246,8 @@ class MeshNet { this.roster = this.getPeerIds(); this.readyPeers.clear(); this.broadcast({ type: 'roster', peers: this.roster.map(id => ({ id, name: this.names.get(id) || '' })) }); + this.readyPeers.clear(); + this._log(`Roster broadcast an ${this.roster.length} Peers`); if (this.onreadyupdate) this.onreadyupdate(0, this.roster.length); } @@ -236,23 +263,27 @@ class MeshNet { this.connections.delete(existing); } this.connections.set(msg.peerId, peer); + this._log(`Identity: ${msg.peerId.slice(0, 8)}`); if (this._pendingPeer && this._pendingPeer.peer === peer) { this._pendingPeer.peerId = msg.peerId; this._pendingPeer.connected = true; this._pendingPeer = null; if (this.onpeerconnect) this.onpeerconnect(msg.peerId); + this._log(`Host: Peer ${msg.peerId.slice(0, 8)} promoted`); if (this.isHost) { setTimeout(() => this._createPendingPeer(), 300); this._broadcastRoster(); } } else { if (this.onpeerconnect) this.onpeerconnect(msg.peerId); + this._log(`Mesh: Peer ${msg.peerId.slice(0, 8)} verbunden`); } return; } if (msg.type === 'signal_relay') { + this._log(`Signal-Relay weitergeleitet an ${msg.to.slice(0, 8)}`); const target = this.connections.get(msg.to) || this._pendingMeshPeers.get(msg.to); if (target) { target.send(JSON.stringify({ @@ -274,11 +305,13 @@ class MeshNet { if (msg.peers) msg.peers.forEach(p => { if (p.name) this.names.set(p.id, p.name); }); this._meshReadySent = false; this._checkMeshConnections(); + this._log(`Roster empfangen: ${this.roster.length} Peers`); return; } if (msg.type === 'mesh_ready' && this.isHost) { this.readyPeers.add(peerId); + this._log(`Mesh ready von ${peerId.slice(0, 8)}`); if (this.onreadyupdate) { this.onreadyupdate(this.readyPeers.size, this.roster.length); } @@ -311,6 +344,7 @@ class MeshNet { const peerId = this._findPeerId(peer); if (peerId) { this.connections.delete(peerId); + this._log(`Peer getrennt: ${peerId.slice(0, 8)}`); if (this.onpeerdisconnect) this.onpeerdisconnect(peerId); } @@ -366,6 +400,7 @@ class MeshNet { } destroy() { + this._log('MeshNet wird zerstört'); for (const peer of this.connections.values()) { try { peer.destroy(); } catch (e) {} } diff --git a/style.css b/style.css index 096c288..f335eba 100644 --- a/style.css +++ b/style.css @@ -476,5 +476,43 @@ footer { background: var(--accent-hover); } +/* Debug pairing log */ +#debug-pairing { + position: fixed; bottom: 0; left: 8px; + width: 320px; max-height: 260px; + background: var(--bg-card); + border: 1px solid var(--border); + border-radius: var(--radius) var(--radius) 0 0; + font-size: 0.75rem; + font-family: monospace; + z-index: 100; + display: none; +} +.debug-mode #debug-pairing { display: flex; flex-direction: column; } +#debug-pairing-header { + padding: 4px 8px; + font-size: 0.7rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-dim); + border-bottom: 1px solid var(--border); + cursor: default; +} +#debug-pairing-messages { + flex: 1; overflow-y: auto; padding: 6px 8px; + max-height: 260px; + display: flex; flex-direction: column; gap: 2px; +} +#debug-pairing-messages div { + padding: 2px 4px; + border-radius: 3px; + line-height: 1.3; + word-break: break-all; +} +#debug-pairing-messages div:nth-child(even) { + background: rgba(255,255,255,0.03); +} + /* Utility */ .hidden { display: none !important; }