UNPKG

@needle-tools/engine

Version:

Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.

160 lines (133 loc) • 4.41 kB
import type { DataConnection, PeerJSOption } from "peerjs"; import Peer, { type PeerConnectOption } from "peerjs"; import { type ConstructorConcrete } from "./engine_types.js"; let peerOptions: PeerJSOption | undefined = undefined; export function getPeerOptions() { return peerOptions; } export function setPeerOptions(opts: PeerJSOption) { peerOptions = opts; } export function getPeerjsInstance(id?: string, opts?: PeerJSOption): Peer { if (!opts) opts = {} opts = { ...peerOptions, ...opts, } if (id) return new Peer(id, opts); return new Peer(opts); } async function importPeer(): Promise<ConstructorConcrete<Peer>> { const pkg = await import("peerjs"); console.log(pkg); if (pkg.default === undefined) return pkg as any; return pkg.default; } enum MessageType { ConnectionList = "connection-list" } declare class InternalPeerMessages { type: MessageType } export class PeerNetworking { get isHost(): boolean { return this._host !== undefined; } private _host?: PeerHost; private _client!: Peer; private _clientData?: DataConnection; constructor() { this.onEnable(); } onEnable() { const hostId = "HOST-5980e65c-8438-453e-8b35-f13c736dcd81"; this.trySetupHost(hostId); } private async trySetupHost(id: string) { const Peer = await importPeer(); const host = new Peer(id); host.on("error", err => { console.error(err); this._host = undefined; this.trySetupClient(id); }); host.on("open", _id => { this._host = new PeerHost(host); }); } private async trySetupClient(hostId: string) { const Peer = await importPeer(); this._client = new Peer(); this._client.on("error", err => { console.error("Client error", err); }); this._client.on("open", id => { console.log("client connected", id); this._clientData = this._client.connect(hostId, { metadata: { id: id } }); this._clientData.on("open", () => { console.log("Connected to host"); }) this._clientData.on("data", data => { console.log("<<", data); }); }); } } abstract class AbstractPeerHandler { protected _peer: Peer; constructor(peer: Peer) { this._peer = peer; } abstract get isHost(): boolean; protected abstract onConnection(con: DataConnection); } //@ts-ignore class PeerClient extends AbstractPeerHandler { get isHost() { return false; } protected onConnection(_con: DataConnection) { } } class PeerHost extends AbstractPeerHandler { get isHost() { return true; } private _connections: DataConnection[] = []; constructor(peer: Peer) { super(peer); console.log("I AM THE HOST"); this._peer?.on("connection", this.onConnection.bind(this)); this._peer.on("close", () => { this.broadcast("BYE"); }); setInterval(() => { this.broadcast("HELLO"); }, 2000); } protected onConnection(con: DataConnection) { console.log("host connection", con); con.on("open", () => { this._connections.push(con); this.broadcastConnection(con); }); } private broadcastConnection(_con: DataConnection) { const connectionIds: string[] = this._connections.map(c => c.metadata?.id).filter(id => id !== undefined); this.broadcast({ "type": MessageType.ConnectionList, "connections": connectionIds }); } private broadcast(msg: any) { if (msg === undefined || msg === null) return; console.log(">>", msg); for (const cur in this._peer.connections) { const curCon = this._peer.connections[cur]; if (!curCon) continue; if (Array.isArray(curCon)) { for (const entry of curCon) { if (!entry) continue; entry.send(msg); } } else { console.warn(curCon); } } } }