@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
104 lines • 4.71 kB
TypeScript
/**
* Per-peer reassembly buffer for fragmented packets.
*
* When a sender's logical payload exceeds the MTU it gets split into
* multiple {@link NetworkPacketType.FRAGMENT} packets, each carrying a
* `(message_id, chunk_index, total_chunks, chunk_bytes)` tuple. The
* receiver feeds those tuples to {@link receive}; once all chunks for a
* `message_id` have arrived, `receive` returns the reassembled bytes
* for the upper layer to dispatch.
*
* Loss recovery via NACK: a partially-received message that doesn't
* complete within `nack_initial_delay_ms` triggers a NACK back to the
* sender listing the missing chunk indices. NACK rounds repeat at
* `nack_resend_interval_ms` up to `nack_max_rounds`; after that the
* receiver gives up on the message and the sender's retention ages
* out independently. Wire the per-tick driver via {@link service}.
*
* Loss-of-last-resort model: if even the NACK retries fail (link is
* dead, sender retention was evicted, etc.) the message never
* completes and FIFO-evicts when the pending cap is hit. Upper layers
* are still expected to tolerate occasional missing messages — NACK
* recovers most loss, not all.
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class FragmentAssembler {
/**
* @param {{
* max_pending_messages?: number,
* max_message_size?: number,
* nack_initial_delay_ms?: number,
* nack_resend_interval_ms?: number,
* nack_max_rounds?: number,
* }} [options]
* `max_pending_messages` caps the number of in-flight reassembly
* slots; when full, the oldest pending message is evicted to make
* room. `max_message_size` rejects reassembled messages larger than
* this many bytes (defensive against a malicious peer or a wedged
* sender). The `nack_*` knobs control the retransmit-request timer
* driven by {@link service}.
*/
constructor({ max_pending_messages, max_message_size, nack_initial_delay_ms, nack_resend_interval_ms, nack_max_rounds, }?: {
max_pending_messages?: number;
max_message_size?: number;
nack_initial_delay_ms?: number;
nack_resend_interval_ms?: number;
nack_max_rounds?: number;
});
/** @type {number} @readonly */
readonly max_pending_messages: number;
/** @type {number} @readonly */
readonly max_message_size: number;
/** @type {number} @readonly */
readonly nack_initial_delay_ms: number;
/** @type {number} @readonly */
readonly nack_resend_interval_ms: number;
/** @type {number} @readonly */
readonly nack_max_rounds: number;
/**
* Process an incoming fragment. Returns the reassembled bytes when
* the message is complete, or null otherwise.
*
* @param {number} message_id sender-assigned message id
* @param {number} chunk_index 0-based
* @param {number} total_chunks expected number of chunks (>= 1)
* @param {Uint8Array} chunk_bytes source byte array
* @param {number} chunk_offset start offset within `chunk_bytes`
* @param {number} chunk_length number of bytes from `chunk_offset` to consume
* @returns {Uint8Array|null}
*/
receive(message_id: number, chunk_index: number, total_chunks: number, chunk_bytes: Uint8Array, chunk_offset: number, chunk_length: number): Uint8Array | null;
/**
* Per-tick maintenance: drive NACK emission for pending messages
* whose initial delay has elapsed and whose resend interval has
* lapsed, and drop messages whose NACK budget is exhausted.
*
* On the first service tick that observes a pending entry, its
* arrival is timestamped (so the initial delay is measured from
* `now_ms` of that tick rather than from the actual wall-clock
* arrival of the first chunk — at typical tick rates the difference
* is one tick, negligible vs. the 100 ms initial delay).
*
* `on_nack` is invoked once per eligible message with
* `(message_id, indices, count)`. The `indices` argument is a
* reused internal Uint8Array; only `indices[0..count)` is valid
* and only for the duration of the call.
*
* @param {number} now_ms
* @param {function(number, Uint8Array, number): void} on_nack
*/
service(now_ms: number, on_nack: (arg0: number, arg1: Uint8Array, arg2: number) => void): void;
/**
* Number of in-flight reassembly slots currently held.
* @returns {number}
*/
pending_count(): number;
/**
* Drop all in-flight reassembly state. Useful on peer disconnect.
*/
clear(): void;
#private;
}
//# sourceMappingURL=FragmentAssembler.d.ts.map