Pairing-Logs in ?debug: _log() in p2p.js, Panel links unten
All checks were successful
Pin to IPFS / pin (push) Successful in 11s
All checks were successful
Pin to IPFS / pin (push) Successful in 11s
This commit is contained in:
parent
f3f43ae68e
commit
becd1593b6
4 changed files with 95 additions and 5 deletions
15
app.js
15
app.js
|
|
@ -281,6 +281,7 @@
|
||||||
mesh.playerName = name;
|
mesh.playerName = name;
|
||||||
mesh.names.set(mesh.myPeerId, name || mesh.myPeerId.slice(0, 8));
|
mesh.names.set(mesh.myPeerId, name || mesh.myPeerId.slice(0, 8));
|
||||||
mesh.onchat = (peerId, text) => addChatMessage(mesh.getPeerName(peerId), text);
|
mesh.onchat = (peerId, text) => addChatMessage(mesh.getPeerName(peerId), text);
|
||||||
|
mesh.onlog = addPairingLog;
|
||||||
|
|
||||||
mesh.onpeerconnect = pid => {
|
mesh.onpeerconnect = pid => {
|
||||||
renderPlayers('host-player-list');
|
renderPlayers('host-player-list');
|
||||||
|
|
@ -348,7 +349,7 @@
|
||||||
mesh.playerName = name;
|
mesh.playerName = name;
|
||||||
mesh.names.set(mesh.myPeerId, name || mesh.myPeerId.slice(0, 8));
|
mesh.names.set(mesh.myPeerId, name || mesh.myPeerId.slice(0, 8));
|
||||||
mesh.onchat = (peerId, text) => addChatMessage(mesh.getPeerName(peerId), text);
|
mesh.onchat = (peerId, text) => addChatMessage(mesh.getPeerName(peerId), text);
|
||||||
show('join');
|
mesh.onlog = addPairingLog;
|
||||||
$('join-answer-section').hidden = true;
|
$('join-answer-section').hidden = true;
|
||||||
$('join-scan-status').textContent = 'QR-Code scannen...';
|
$('join-scan-status').textContent = 'QR-Code scannen...';
|
||||||
|
|
||||||
|
|
@ -438,6 +439,18 @@
|
||||||
show('start');
|
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 ─── */
|
/* ─── Debug chat ─── */
|
||||||
const chatInput = $('debug-chat-text');
|
const chatInput = $('debug-chat-text');
|
||||||
const chatMessages = $('debug-chat-messages');
|
const chatMessages = $('debug-chat-messages');
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,10 @@
|
||||||
<button id="debug-chat-send">Senden</button>
|
<button id="debug-chat-send">Senden</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="debug-pairing" class="debug-only">
|
||||||
|
<div id="debug-pairing-header">Pairing-Logs</div>
|
||||||
|
<div id="debug-pairing-messages"></div>
|
||||||
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<p>© by neunzig.wtf · commit <a id="commit-hash" href="https://git.ziemlichoptimal.de/90wtf/randomp2p/src/commit/HASH">HASH</a></p>
|
<p>© by neunzig.wtf · commit <a id="commit-hash" href="https://git.ziemlichoptimal.de/90wtf/randomp2p/src/commit/HASH">HASH</a></p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
|
||||||
43
p2p.js
43
p2p.js
|
|
@ -22,8 +22,11 @@ class MeshNet {
|
||||||
this.onanswerready = null;
|
this.onanswerready = null;
|
||||||
this.onreadyupdate = null;
|
this.onreadyupdate = null;
|
||||||
this.onchat = null;
|
this.onchat = null;
|
||||||
|
this.onlog = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_log(msg) { if (this.onlog) this.onlog(msg); }
|
||||||
|
|
||||||
/* ─── Host: room ─── */
|
/* ─── Host: room ─── */
|
||||||
|
|
||||||
createRoom() {
|
createRoom() {
|
||||||
|
|
@ -42,6 +45,7 @@ class MeshNet {
|
||||||
peer.on('signal', async signal => {
|
peer.on('signal', async signal => {
|
||||||
if (signalSent) return;
|
if (signalSent) return;
|
||||||
signalSent = true;
|
signalSent = true;
|
||||||
|
this._log('SDP-Signal (Offer) empfangen');
|
||||||
|
|
||||||
const minSignal = signal.sdp ? { ...signal, sdp: stripSDP(signal.sdp) } : signal;
|
const minSignal = signal.sdp ? { ...signal, sdp: stripSDP(signal.sdp) } : signal;
|
||||||
const packed = await pack({ v: 1, id: this.myPeerId, s: minSignal });
|
const packed = await pack({ v: 1, id: this.myPeerId, s: minSignal });
|
||||||
|
|
@ -49,10 +53,12 @@ class MeshNet {
|
||||||
this._fullSDPData = this._currentQRData;
|
this._fullSDPData = this._currentQRData;
|
||||||
|
|
||||||
if (this.onqrupdate) this.onqrupdate(this._currentQRData);
|
if (this.onqrupdate) this.onqrupdate(this._currentQRData);
|
||||||
|
this._log('Offer QR erstellt');
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('connect', () => {
|
peer.on('connect', () => {
|
||||||
peer.send(JSON.stringify({ type: 'identity', peerId: this.myPeerId, name: this.playerName }));
|
peer.send(JSON.stringify({ type: 'identity', peerId: this.myPeerId, name: this.playerName }));
|
||||||
|
this._log('DataChannel offen, sende Identity');
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('data', data => {
|
peer.on('data', data => {
|
||||||
|
|
@ -71,14 +77,19 @@ class MeshNet {
|
||||||
}
|
}
|
||||||
|
|
||||||
async feedAnswer(answerStr) {
|
async feedAnswer(answerStr) {
|
||||||
if (!this._pendingPeer || this._pendingPeer.signalReceived) return;
|
if (!this._pendingPeer || this._pendingPeer.signalReceived) {
|
||||||
|
this._log('feedAnswer ignoriert (bereits verbunden)');
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const data = await unpack(decode45(answerStr));
|
const data = await unpack(decode45(answerStr));
|
||||||
this._pendingPeer.peerId = data.id;
|
this._pendingPeer.peerId = data.id;
|
||||||
this._pendingPeer.peer.signal(data.s);
|
this._pendingPeer.peer.signal(data.s);
|
||||||
this._pendingPeer.signalReceived = true;
|
this._pendingPeer.signalReceived = true;
|
||||||
|
this._log(`Answer verarbeitet von ${data.id.slice(0, 8)}`);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('feedAnswer error:', e);
|
console.error('feedAnswer error:', e);
|
||||||
|
this._log(`feedAnswer Fehler: ${e.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -89,6 +100,7 @@ class MeshNet {
|
||||||
const remoteId = data.id;
|
const remoteId = data.id;
|
||||||
this.hostPeerId = remoteId;
|
this.hostPeerId = remoteId;
|
||||||
const offer = data.s;
|
const offer = data.s;
|
||||||
|
this._log(`QR gelesen, signalisiere Host ${remoteId.slice(0, 8)}`);
|
||||||
|
|
||||||
const peer = new SimplePeer({ initiator: false, trickle: false, config: { iceServers: [] } });
|
const peer = new SimplePeer({ initiator: false, trickle: false, config: { iceServers: [] } });
|
||||||
let answerSent = false;
|
let answerSent = false;
|
||||||
|
|
@ -100,11 +112,13 @@ class MeshNet {
|
||||||
const packed = await pack({ v: 1, id: this.myPeerId, s: minSignal, to: remoteId });
|
const packed = await pack({ v: 1, id: this.myPeerId, s: minSignal, to: remoteId });
|
||||||
const answerData = encode45(packed);
|
const answerData = encode45(packed);
|
||||||
if (this.onanswerready) this.onanswerready(answerData);
|
if (this.onanswerready) this.onanswerready(answerData);
|
||||||
|
this._log('Answer QR erstellt');
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('connect', () => {
|
peer.on('connect', () => {
|
||||||
this.connections.set(remoteId, peer);
|
this.connections.set(remoteId, peer);
|
||||||
peer.send(JSON.stringify({ type: 'identity', peerId: this.myPeerId, name: this.playerName }));
|
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);
|
if (this.onpeerconnect) this.onpeerconnect(remoteId);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -116,7 +130,10 @@ class MeshNet {
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('close', () => this._cleanupPeer(peer));
|
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);
|
peer.signal(offer);
|
||||||
this.connections.set(remoteId, peer);
|
this.connections.set(remoteId, peer);
|
||||||
|
|
@ -134,7 +151,7 @@ class MeshNet {
|
||||||
|
|
||||||
peer.on('connect', () => {
|
peer.on('connect', () => {
|
||||||
if (onConnect) onConnect();
|
if (onConnect) onConnect();
|
||||||
// No identity exchange – peerId known from context
|
this._log('DataChannel (Mesh) offen');
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('data', data => {
|
peer.on('data', data => {
|
||||||
|
|
@ -145,7 +162,10 @@ class MeshNet {
|
||||||
});
|
});
|
||||||
|
|
||||||
peer.on('close', () => this._cleanupPeer(peer));
|
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;
|
return peer;
|
||||||
}
|
}
|
||||||
|
|
@ -154,6 +174,7 @@ class MeshNet {
|
||||||
if (this.connections.has(targetPeerId)) return;
|
if (this.connections.has(targetPeerId)) return;
|
||||||
if (this._pendingMeshPeers.has(targetPeerId)) return;
|
if (this._pendingMeshPeers.has(targetPeerId)) return;
|
||||||
if (this.myPeerId >= targetPeerId) return;
|
if (this.myPeerId >= targetPeerId) return;
|
||||||
|
this._log(`Mesh zu ${targetPeerId.slice(0, 8)} initiiert`);
|
||||||
|
|
||||||
const peer = this._createSimplePeer(true,
|
const peer = this._createSimplePeer(true,
|
||||||
signal => {
|
signal => {
|
||||||
|
|
@ -171,6 +192,7 @@ class MeshNet {
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleRelayedSignal(fromPeerId, signal) {
|
_handleRelayedSignal(fromPeerId, signal) {
|
||||||
|
this._log(`Relayed-Signal von ${fromPeerId.slice(0, 8)}`);
|
||||||
let peer = this.connections.get(fromPeerId) || this._pendingMeshPeers.get(fromPeerId);
|
let peer = this.connections.get(fromPeerId) || this._pendingMeshPeers.get(fromPeerId);
|
||||||
|
|
||||||
if (!peer) {
|
if (!peer) {
|
||||||
|
|
@ -192,6 +214,8 @@ class MeshNet {
|
||||||
}
|
}
|
||||||
|
|
||||||
_checkMeshConnections() {
|
_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) {
|
for (const pid of this.roster) {
|
||||||
if (pid === this.myPeerId) continue;
|
if (pid === this.myPeerId) continue;
|
||||||
if (this.connections.has(pid)) continue;
|
if (this.connections.has(pid)) continue;
|
||||||
|
|
@ -214,6 +238,7 @@ class MeshNet {
|
||||||
if (allConnected && this.roster.length > 0) {
|
if (allConnected && this.roster.length > 0) {
|
||||||
this._meshReadySent = true;
|
this._meshReadySent = true;
|
||||||
this.sendTo(this.hostPeerId, { type: 'mesh_ready' });
|
this.sendTo(this.hostPeerId, { type: 'mesh_ready' });
|
||||||
|
this._log('Mesh ready gesendet');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -221,6 +246,8 @@ class MeshNet {
|
||||||
this.roster = this.getPeerIds();
|
this.roster = this.getPeerIds();
|
||||||
this.readyPeers.clear();
|
this.readyPeers.clear();
|
||||||
this.broadcast({ type: 'roster', peers: this.roster.map(id => ({ id, name: this.names.get(id) || '' })) });
|
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);
|
if (this.onreadyupdate) this.onreadyupdate(0, this.roster.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -236,23 +263,27 @@ class MeshNet {
|
||||||
this.connections.delete(existing);
|
this.connections.delete(existing);
|
||||||
}
|
}
|
||||||
this.connections.set(msg.peerId, peer);
|
this.connections.set(msg.peerId, peer);
|
||||||
|
this._log(`Identity: ${msg.peerId.slice(0, 8)}`);
|
||||||
|
|
||||||
if (this._pendingPeer && this._pendingPeer.peer === peer) {
|
if (this._pendingPeer && this._pendingPeer.peer === peer) {
|
||||||
this._pendingPeer.peerId = msg.peerId;
|
this._pendingPeer.peerId = msg.peerId;
|
||||||
this._pendingPeer.connected = true;
|
this._pendingPeer.connected = true;
|
||||||
this._pendingPeer = null;
|
this._pendingPeer = null;
|
||||||
if (this.onpeerconnect) this.onpeerconnect(msg.peerId);
|
if (this.onpeerconnect) this.onpeerconnect(msg.peerId);
|
||||||
|
this._log(`Host: Peer ${msg.peerId.slice(0, 8)} promoted`);
|
||||||
if (this.isHost) {
|
if (this.isHost) {
|
||||||
setTimeout(() => this._createPendingPeer(), 300);
|
setTimeout(() => this._createPendingPeer(), 300);
|
||||||
this._broadcastRoster();
|
this._broadcastRoster();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.onpeerconnect) this.onpeerconnect(msg.peerId);
|
if (this.onpeerconnect) this.onpeerconnect(msg.peerId);
|
||||||
|
this._log(`Mesh: Peer ${msg.peerId.slice(0, 8)} verbunden`);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type === 'signal_relay') {
|
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);
|
const target = this.connections.get(msg.to) || this._pendingMeshPeers.get(msg.to);
|
||||||
if (target) {
|
if (target) {
|
||||||
target.send(JSON.stringify({
|
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); });
|
if (msg.peers) msg.peers.forEach(p => { if (p.name) this.names.set(p.id, p.name); });
|
||||||
this._meshReadySent = false;
|
this._meshReadySent = false;
|
||||||
this._checkMeshConnections();
|
this._checkMeshConnections();
|
||||||
|
this._log(`Roster empfangen: ${this.roster.length} Peers`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg.type === 'mesh_ready' && this.isHost) {
|
if (msg.type === 'mesh_ready' && this.isHost) {
|
||||||
this.readyPeers.add(peerId);
|
this.readyPeers.add(peerId);
|
||||||
|
this._log(`Mesh ready von ${peerId.slice(0, 8)}`);
|
||||||
if (this.onreadyupdate) {
|
if (this.onreadyupdate) {
|
||||||
this.onreadyupdate(this.readyPeers.size, this.roster.length);
|
this.onreadyupdate(this.readyPeers.size, this.roster.length);
|
||||||
}
|
}
|
||||||
|
|
@ -311,6 +344,7 @@ class MeshNet {
|
||||||
const peerId = this._findPeerId(peer);
|
const peerId = this._findPeerId(peer);
|
||||||
if (peerId) {
|
if (peerId) {
|
||||||
this.connections.delete(peerId);
|
this.connections.delete(peerId);
|
||||||
|
this._log(`Peer getrennt: ${peerId.slice(0, 8)}`);
|
||||||
if (this.onpeerdisconnect) this.onpeerdisconnect(peerId);
|
if (this.onpeerdisconnect) this.onpeerdisconnect(peerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -366,6 +400,7 @@ class MeshNet {
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
|
this._log('MeshNet wird zerstört');
|
||||||
for (const peer of this.connections.values()) {
|
for (const peer of this.connections.values()) {
|
||||||
try { peer.destroy(); } catch (e) {}
|
try { peer.destroy(); } catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
38
style.css
38
style.css
|
|
@ -476,5 +476,43 @@ footer {
|
||||||
background: var(--accent-hover);
|
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 */
|
/* Utility */
|
||||||
.hidden { display: none !important; }
|
.hidden { display: none !important; }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue