const b4a = window.require('b4a'); const crypto = window.require('crypto'); const Hyperswarm = window.require('hyperswarm'); export async function initNetwork() { // Kept for legacy compatibility if imported elsewhere } class P2PNetwork { constructor() { this.swarm = null; this.peers = new Set(); this.onPeerConnect = null; this.onPeerDisconnect = null; } async initialize() { try { this.swarm = new Hyperswarm(); this.swarm.on('connection', (conn, info) => { const peerKey = b4a.toString(info.publicKey, 'hex'); this.peers.add(peerKey); if (this.onPeerConnect) this.onPeerConnect(peerKey); conn.on('close', () => { this.peers.delete(peerKey); if (this.onPeerDisconnect) this.onPeerDisconnect(peerKey); }); conn.on('data', (data) => { console.log(`Received data from ${peerKey}:`, data.toString()); }); }); console.log('P2P Network Initialized'); } catch (err) { console.error('Failed to initialize Hyperswarm.', err); } } async joinGlobalServer() { if (!this.swarm) return; const globalTopicSeed = crypto.createHash('sha256').update('GLOBAL_MAIN_SERVER_V1').digest(); const discovery = this.swarm.join(globalTopicSeed, { client: true, server: true }); await discovery.flushed(); console.log('Joined Global Main Server Swarm'); } } export const networkLegacy = new P2PNetwork(); --- START OF FILE src/p2p/modules/discovery.js --- const b4a = window.require('b4a'); import { generateUUID, sodium } from '../utils.js'; export async function searchUser(network, targetUsername) { const normalized = targetUsername.toLowerCase(); if (network.userDirectory.has(normalized)) { return network.userDirectory.get(normalized); } const topic = b4a.alloc(32); sodium.crypto_generichash(topic, b4a.from('peercord-user:' + normalized)); network.swarm.join(topic, { client: true, server: false }); return new Promise((resolve) => { let resolved = false; const finish = (result) => { if (resolved) return; resolved = true; network.swarm.leave(topic); resolve(result); }; const timeout = setTimeout(() => { finish(null); }, 5000); const interval = setInterval(() => { if (network.userDirectory.has(normalized)) { clearTimeout(timeout); clearInterval(interval); finish(network.userDirectory.get(normalized)); } }, 500); const queryId = generateUUID(); network.pendingWhois.set(queryId, (result) => { clearTimeout(timeout); clearInterval(interval); finish(result); }); const msg = b4a.from(JSON.stringify({ type: 'whois', queryId, username: normalized })); for (const { conn } of network.peers.values()) { conn.write(msg); } }); } export async function queueFriendRequest(network, targetUsername) { const uname = targetUsername.toLowerCase(); network.pendingFriendRequests.add(uname); await network.pendingRequestsDb.put(uname, { timestamp: Date.now() }); const topic = b4a.alloc(32); sodium.crypto_generichash(topic, b4a.from('peercord-user:' + uname)); network.swarm.join(topic, { client: true, server: false }); } export async function trackPeerCore(network, coreKeyHex) { if (network.peerCores.has(coreKeyHex)) return; const core = network.store.get({ key: b4a.from(coreKeyHex, 'hex'), valueEncoding: 'json' }); await core.ready(); network.peerCores.set(coreKeyHex, core); let processedSeq = -1; for (let i = 0; i < core.length; i++) { const msg = await core.get(i); network.processMessage(msg); processedSeq = i; } core.on('append', async () => { network._emitSync(); for (let i = processedSeq + 1; i < core.length; i++) { const msg = await core.get(i); network.processMessage(msg); processedSeq = i; } }); } --- START OF FILE src/p2p/modules/files.js --- const b4a = window.require('b4a'); import { generateUUID, fs, path, os } from '../utils.js'; async function _hostFile(network, id, fileObj, fileCore) { network.transfers[id] = { progress: 0, speed: 0, state: 'processing' }; if (network.onTransfersUpdate) network.onTransfersUpdate(network.transfers); let processedBytes = 0; let lastTime = Date.now(); let lastBytes = 0; const updateProcessingProgress = (chunkLength) => { processedBytes += chunkLength; const now = Date.now(); if (now - lastTime >= 250 || processedBytes >= fileObj.size) { const timeDiff = (now - lastTime) / 1000; const speed = timeDiff > 0 ? (processedBytes - lastBytes) / timeDiff : 0; const progress = Math.min(1, processedBytes / fileObj.size); network.transfers[id] = { progress, speed, state: 'processing' }; if (network.onTransfersUpdate) network.onTransfersUpdate(network.transfers); lastTime = now; lastBytes = processedBytes; } }; if (fileObj.path && fs) { const stream = fs.createReadStream(fileObj.path, { highWaterMark: 64 * 1024 }); for await (const chunk of stream) { await fileCore.append(chunk); updateProcessingProgress(chunk.length); } } else if (fileObj.fileObj && typeof fileObj.fileObj.stream === 'function') { const stream = fileObj.fileObj.stream(); const reader = stream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; await fileCore.append(b4a.from(value)); updateProcessingProgress(value.length); } } else if (fileObj.buffer) { const buf = b4a.from(fileObj.buffer); const chunkSize = 64 * 1024; for(let i=0; i= fileMeta.size) { const msg = network.messages.get(msgId); if (msg) { msg.localPath = filePath; msg.isMediaInDB = isMedia; network._emitMessages(); } await network.localFilesDb.put(msgId, filePath); network.transfers[msgId] = { progress: 1, speed: 0, state: 'completed' }; if (network.onTransfersUpdate) network.onTransfersUpdate(network.transfers); return; } else { try { fs.unlinkSync(filePath); } catch(e) {} } } network.transfers[msgId] = { progress: 0, speed: 0, state: 'downloading' }; if (network.onTransfersUpdate) network.onTransfersUpdate(network.transfers); const readStream = core.createReadStream({ live: true }); const writeStream = fs.createWriteStream(filePath); let downloadedBytes = 0; let lastTime = Date.now(); let lastBytes = 0; let isFinished = false; const sendProgress = (progress, speed) => { network.transfers[msgId] = { progress, speed, state: 'downloading' }; if (network.onTransfersUpdate) network.onTransfersUpdate(network.transfers); network.sendEphemeral({ type: 'transfer_progress', id: msgId, progress, speed }); }; writeStream.on('finish', async () => { const msg = network.messages.get(msgId); if (msg) { msg.localPath = filePath; msg.isMediaInDB = isMedia; network._emitMessages(); } await network.localFilesDb.put(msgId, filePath); network.transfers[msgId] = { progress: 1, speed: 0, state: 'completed' }; if (network.onTransfersUpdate) network.onTransfersUpdate(network.transfers); sendProgress(1, 0); }); writeStream.on('error', (err) => { console.error("File write error:", err); }); if (fileMeta.size === 0) { writeStream.end(); return; } readStream.on('data', (chunk) => { if (isFinished) return; downloadedBytes += chunk.length; writeStream.write(chunk); const now = Date.now(); if (now - lastTime >= 500 || downloadedBytes >= fileMeta.size) { const timeDiff = (now - lastTime) / 1000; const speed = timeDiff > 0 ? (downloadedBytes - lastBytes) / timeDiff : 0; const progress = Math.min(1, downloadedBytes / fileMeta.size); sendProgress(progress, Math.max(0, speed)); lastTime = now; lastBytes = downloadedBytes; } if (downloadedBytes >= fileMeta.size) { isFinished = true; readStream.destroy(); writeStream.end(); } }); }