#!/usr/bin/env node
/**
* Minimal Hyperswarm ephemeral ping/pong — cross-machine reachability test.
*
* Exercises the SAME network path the game's HyperswarmMultiplayerPeer
* GDExtension uses: HyperDHT discovery + NAT hole-punch + UDX + Noise.
* If two machines connect + exchange pings here, the network/DHT path is
* fine and any in-game mesh failure is in the extension or its usage.
* If they DON'T connect here, it's a network/NAT/DHT problem.
*
* Usage (run on each machine, same topic):
* node mesh-ping.cjs server # announces, echoes pings
* node mesh-ping.cjs client # looks up, sends pings, prints RTT
* node mesh-ping.cjs both # localhost sanity (real DHT)
*
* Optional env TOPIC to override the rendezvous string (default below).
*/
const Hyperswarm = require('hyperswarm')
const crypto = require('crypto')
const os = require('os')
const mode = process.argv[2] || 'both'
const topicStr = process.env.TOPIC || 'hyper-tps-mesh-ping-2026'
const TOPIC = crypto.createHash('sha256').update(topicStr).digest()
const host = os.hostname()
console.log(`[${host}] mode=${mode} topic="${topicStr}" (${TOPIC.toString('hex').slice(0, 16)})`)
function server () {
const swarm = new Hyperswarm()
swarm.on('connection', (conn, info) => {
const pk = info.publicKey.toString('hex').slice(0, 16)
console.log(`[${host}] SERVER <- connection from ${pk}`)
conn.on('data', (buf) => { try { conn.write(buf) } catch (e) {} }) // echo
conn.on('error', () => {})
conn.on('close', () => console.log(`[${host}] SERVER conn closed ${pk}`))
})
swarm.join(TOPIC, { server: true, client: false }).flushed().then(() => {
console.log(`[${host}] SERVER announced on DHT, waiting for peers...`)
})
}
function client () {
const swarm = new Hyperswarm()
let connected = false
swarm.on('connection', (conn, info) => {
const pk = info.publicKey.toString('hex').slice(0, 16)
connected = true
console.log(`[${host}] CLIENT -> connected to ${pk}`)
let seq = 0
let t0 = 0n
const send = () => {
const b = Buffer.alloc(8)
b.writeUInt32LE(0xBEEF, 0)
b.writeUInt32LE(seq++, 4)
t0 = process.hrtime.bigint()
try { conn.write(b) } catch (e) {}
}
conn.on('data', (buf) => {
const rtt = Number(process.hrtime.bigint() - t0) / 1e6
console.log(`[${host}] PONG seq=${buf.readUInt32LE(4)} rtt=${rtt.toFixed(2)}ms`)
setTimeout(send, 1000)
})
conn.on('error', () => {})
send()
})
swarm.join(TOPIC, { server: false, client: true })
console.log(`[${host}] CLIENT looking up peers on DHT...`)
setInterval(() => {
if (!connected) console.log(`[${host}] CLIENT still searching... (conns=${swarm.connections.size})`)
}, 5000)
}
if (mode === 'server') server()
else if (mode === 'client') client()
else { server(); setTimeout(client, 1500) }