UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

78 lines (68 loc) 2.66 kB
import { seq16_distance } from "./seq16.js"; /** * Ack bitfield encoding, after the Quake 3 / Glenn Fiedler "ack reliability" trick. * * Each outgoing packet carries a header `(latest_received_seq, ack_bitfield)`. * `ack_bitfield` is a 32-bit unsigned integer in which bit `i` is set if the receiver * has received the packet with sequence number `latest_received_seq - 1 - i`. * * This means a single 16-bit + 32-bit pair conveys positive acknowledgement of up * to 33 packets in one go, providing very robust ack delivery even under packet * loss — losing one ack-bearing packet typically just means the receiver has * already confirmed the same packets via a later ack-bearing packet. * * All functions are pure. The bitfield is treated as unsigned via `>>> 0`. * * @author Alex Goldring * @copyright Company Named Limited (c) 2025 */ /** * Build an ack bitfield from the current "latest received" seq and a callback that * answers "did we receive seq N?". * * @param {number} latest_received_seq 16-bit sequence * @param {function(number): boolean} was_received * @returns {number} unsigned 32-bit bitfield */ export function ack_bitfield_build(latest_received_seq, was_received) { let bits = 0; for (let i = 0; i < 32; i++) { const seq = (latest_received_seq - 1 - i) & 0xFFFF; if (was_received(seq)) { bits |= (1 << i); } } return bits >>> 0; } /** * Test whether the bitfield reports a specific sequence as received. * * @param {number} latest_received_seq 16-bit sequence * @param {number} bitfield unsigned 32-bit * @param {number} seq 16-bit sequence to test * @returns {boolean} */ export function ack_bitfield_contains(latest_received_seq, bitfield, seq) { if (seq === latest_received_seq) return true; const offset = seq16_distance(seq, latest_received_seq) - 1; if (offset < 0 || offset >= 32) return false; return ((bitfield >>> offset) & 1) === 1; } /** * Iterate every sequence number considered acknowledged by the given header, * invoking `fn(seq)` for each in newest-to-oldest order. `latest_received_seq` * is yielded first (it is acked by definition), followed by each older seq * whose bit is set in the bitfield. * * @param {number} latest_received_seq 16-bit sequence * @param {number} bitfield unsigned 32-bit * @param {function(number): void} fn */ export function ack_bitfield_for_each(latest_received_seq, bitfield, fn) { fn(latest_received_seq & 0xFFFF); for (let i = 0; i < 32; i++) { if (((bitfield >>> i) & 1) === 1) { fn((latest_received_seq - 1 - i) & 0xFFFF); } } }