@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
103 lines • 4.01 kB
TypeScript
/**
* Per-peer sender-side retention buffer for fragmented message bytes,
* used to satisfy NACK retransmits from the peer's {@link FragmentAssembler}.
*
* Lifecycle:
* 1. {@link retain} — called on each fragmented send; copies the source
* bytes into the retention so they're available to retransmit. If
* the per-peer slot cap is hit, the oldest retained entry is FIFO-
* evicted.
* 2. {@link consume_nack} — looks up retained bytes for a NACK target
* and bumps the retry counter. Returns `null` if the entry is gone
* (aged out, evicted, or out of retry budget — in which case the
* entry is dropped here too).
* 3. {@link service} — call once per tick to age out entries older
* than `max_age_ms`.
* 4. {@link clear} — drop everything; call on peer disconnect.
*
* Memory cost: each retained entry holds a fresh copy of its source
* payload (the upstream send buffer is a reused scratch). With the
* default 16 slots × up-to-64 KiB cap on the receiver, worst case is
* 1 MiB per peer; steady-state is empty because steady traffic fits in
* one packet and bypasses fragmentation.
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class FragmentRetention {
/**
* @param {{
* max_messages?: number,
* max_age_ms?: number,
* max_retries?: number,
* }} [options]
* `max_messages` caps simultaneously-retained messages; oldest is
* FIFO-evicted on overflow. `max_age_ms` is the per-entry TTL.
* `max_retries` caps how many NACK retransmit rounds a single
* retained message can satisfy before being dropped.
*/
constructor({ max_messages, max_age_ms, max_retries, }?: {
max_messages?: number;
max_age_ms?: number;
max_retries?: number;
});
/** @type {number} @readonly */
readonly max_messages: number;
/** @type {number} @readonly */
readonly max_age_ms: number;
/** @type {number} @readonly */
readonly max_retries: number;
/**
* Copy `payload[0..length)` into retention under `message_id`.
* Evicts the oldest retained entry if `max_messages` is exceeded.
*
* If a retained entry already exists for `message_id` (only legitimate
* cause: uint16 wrap), the old entry is dropped first so the FIFO
* position and retry budget restart from the new send.
*
* @param {number} message_id
* @param {Uint8Array} payload source bytes (caller may overwrite after return)
* @param {number} length number of bytes from `payload[0]` to copy
* @param {number} now_ms monotonic timestamp for age-out
*/
retain(message_id: number, payload: Uint8Array, length: number, now_ms: number): void;
/**
* Mark a NACK round for `message_id` and return the retained entry
* so the caller can re-emit the missing chunks. Returns `null` if
* no entry exists, or if the per-message retry budget is now
* exhausted (in which case the entry is dropped).
*
* @param {number} message_id
* @returns {{payload: Uint8Array, length: number}|null}
*/
consume_nack(message_id: number): {
payload: Uint8Array;
length: number;
} | null;
/**
* Per-tick maintenance: evict any entry older than `max_age_ms`.
*
* The FIFO is in strict insertion order, so we can stop at the first
* still-fresh entry rather than scanning the whole map.
*
* @param {number} now_ms
*/
service(now_ms: number): void;
/**
* Number of currently-retained messages.
* @returns {number}
*/
size(): number;
/**
* Whether `message_id` is currently retained.
* @param {number} message_id
* @returns {boolean}
*/
has(message_id: number): boolean;
/**
* Drop every retained entry. Use on peer disconnect.
*/
clear(): void;
#private;
}
//# sourceMappingURL=FragmentRetention.d.ts.map