UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

120 lines (109 loc) 4.3 kB
import Signal from "../../../core/events/signal/Signal.js"; /** * Base class for transport adapters that ferry opaque byte buffers between two * endpoints. Assumes UDP-style semantics: unreliable, unordered, no flow * control. Reliability or ordering guarantees layer above (see {@link Channel}). * * Subclasses override `send` (mandatory) and may override `connect`, * `disconnect`, or `getStats` (defaults provided). They can write to * `onReceive` directly when bytes arrive. * * Concrete subclasses in this codebase: * - {@link LoopbackTransport} — paired in-process queue for unit tests * - {@link SimulatedTransport} — paired in-process queue with simulated * latency / jitter / loss for prototypes and stress tests * - {@link WebRTCDataChannelTransport} — browser RTCDataChannel wrapper * - {@link WebTransportTransport} — browser WebTransport datagrams (HTTP/3) * - {@link NodeUDPTransport} — Node.js dgram wrapper * - {@link WebSocketTransport} — browser WebSocket wrapper (lobby/chat only; * not for game state — TCP head-of-line blocking) * * The duck-typed contract was previously documented here as JSDoc; it has been * promoted to this concrete base so that `instanceof Transport` works, IDEs * complete the interface, and the relationship between adapters is visible. * * @author Alex Goldring * @copyright Company Named Limited (c) 2025 */ export class Transport { /** * Whether this transport guarantees delivery of every packet sent. * `true` for TCP-style (WebSocket, WebTransport streams); * `false` for UDP-style (raw UDP, WebRTC unreliable data channels, * WebTransport datagrams). * * Higher layers (e.g. `Channel`, `ReliableCommandPipeline`) can * skip retransmit / dedup work when this is `true`. Defaults to * `false` (the conservative assumption — assume loss is possible). * @type {boolean} */ reliable = false; /** * Whether this transport guarantees in-order delivery between any * two packets sent on it. `true` for TCP-style; * `false` for UDP-style. * * Higher layers can skip reorder buffering when this is `true`. * Defaults to `false`. * @type {boolean} */ ordered = false; /** * Fired when a packet arrives. Handlers receive `(bytes, length)` where * `bytes` is a `Uint8Array` valid for the duration of the call only; * copy if you need to keep it. * @type {Signal} */ onReceive = new Signal(); /** * Fired when the transport detects that its connection has been lost * (peer-disconnected, link-dropped, socket-closed). Subscribers * (typically {@link NetworkSession}) react by entering the * reconnect ladder. Adapters that never detect this on their own * (loopback) only fire when explicitly told to. * * Handlers receive `(reason)` where `reason` is an optional * implementation-specific string for diagnostics. * * @type {Signal} */ onDisconnect = new Signal(); /** * Synchronously enqueue `length` bytes from `bytes` for delivery. * Caller may reuse `bytes` immediately after the call returns; transport * implementations must copy if they need to defer delivery. * * @param {Uint8Array} bytes * @param {number} length * @returns {void} */ send(bytes, length) { throw new Error('Transport subclasses must override send(bytes, length).'); } /** * Optional connection lifecycle. Default no-op for transports that are * already-open at construction (e.g. in-process loopback). * @returns {Promise<void>|void} */ connect() { } /** * Optional teardown. Default no-op. */ disconnect() { } /** * Read-only stats snapshot. Default returns zeroes; subclasses are expected * to track their own counters. * @returns {{bytes_in: number, bytes_out: number, packets_in: number, packets_out: number}} */ getStats() { return { bytes_in: 0, bytes_out: 0, packets_in: 0, packets_out: 0 }; } } /** * Fast type check; cheaper than `instanceof Transport` in tight loops. * @readonly * @type {boolean} */ Transport.prototype.isTransport = true;