barkour/js/net.js
w1n5t0n 2f60136b36 feat: initial Barkour — movement-PvP dog fighting game
5 dogs with distinct movement physics, seeded procedural arenas with
traps and mineable powerups, smash-style knockback/stocks combat,
local 2P and P2P netplay (PeerJS room keys), procedural art and sfx,
headless sim smoke tests.
2026-06-10 14:26:58 +02:00

70 lines
2.8 KiB
JavaScript

// P2P networking via PeerJS (WebRTC data channels). The PeerJS public cloud
// broker is used only for signaling/room discovery — once connected, all game
// traffic flows peer-to-peer. No game server anywhere.
//
// The room key IS the host's peer id (prefixed). Share the 5 letters, done.
const PRE = 'barkour-w1-';
const KEY_ALPHABET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
export function randomKey() {
let s = '';
for (let i = 0; i < 5; i++) s += KEY_ALPHABET[Math.floor(Math.random() * KEY_ALPHABET.length)];
return s;
}
export function peerAvailable() {
return typeof window !== 'undefined' && typeof window.Peer !== 'undefined';
}
function errMsg(e) {
const t = e && e.type;
if (t === 'peer-unavailable') return 'No room with that key. Check the code and try again.';
if (t === 'unavailable-id') return 'Room key collision — host again for a fresh key.';
if (t === 'network' || t === 'server-error' || t === 'socket-error') return 'Could not reach the signaling broker (offline?).';
return 'Connection error: ' + (e && e.message || t || 'unknown');
}
// cb: { open(key), conn(), data(msg), close(reason) }
export function hostRoom(key, cb) {
if (!peerAvailable()) { cb.close('PeerJS failed to load — check your internet connection.'); return null; }
const peer = new window.Peer(PRE + key);
let conn = null;
let dead = false;
const die = (msg) => { if (!dead) { dead = true; cb.close(msg); } };
peer.on('open', () => cb.open(key));
peer.on('error', (e) => die(errMsg(e)));
peer.on('disconnected', () => { try { peer.reconnect(); } catch (_) { /* broker gone; game traffic is p2p anyway */ } });
peer.on('connection', (c) => {
if (conn) { try { c.close(); } catch (_) {} return; } // room is full (2 players)
conn = c;
c.on('open', () => cb.conn());
c.on('data', (d) => cb.data(d));
c.on('close', () => die('Other player disconnected.'));
c.on('error', () => die('Connection lost.'));
});
return {
send: (o) => { if (conn && conn.open) conn.send(o); },
destroy: () => { dead = true; try { peer.destroy(); } catch (_) {} },
};
}
export function joinRoom(key, cb) {
if (!peerAvailable()) { cb.close('PeerJS failed to load — check your internet connection.'); return null; }
const peer = new window.Peer();
let dead = false;
const die = (msg) => { if (!dead) { dead = true; cb.close(msg); } };
const api = {
send: () => {},
destroy: () => { dead = true; try { peer.destroy(); } catch (_) {} },
};
peer.on('error', (e) => die(errMsg(e)));
peer.on('open', () => {
const c = peer.connect(PRE + key.toUpperCase().trim(), { reliable: true });
c.on('open', () => { api.send = (o) => { if (c.open) c.send(o); }; cb.conn(); });
c.on('data', (d) => cb.data(d));
c.on('close', () => die('Host disconnected.'));
c.on('error', () => die('Connection lost.'));
});
return api;
}