UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

102 lines (92 loc) 4.41 kB
/** * Packet-size constants for the fragmentation layer. * * The target transport MTU is 1200 bytes (Quake / Source / Glenn Fiedler's * Networked Physics recommend ~1200 to stay under typical PMTU after IP/UDP * headers; ethernet 1500 - 40 IPv6 - 8 UDP = 1452 in the best case, but * intermediate hops with tunnelling, IPv6 extension headers, or DSLite * routinely shave this down — 1200 is the safe number). * * Above this, the application's logical payload must be split into chunks * via {@link send_fragmented} and reassembled on the receiver via * {@link FragmentAssembler}. * * Header overhead breakdown for a fragment packet, from the transport's * point of view: * - {@link Channel} adds 8 bytes (seq + ack_latest + ack_bitfield). * - The FRAGMENT packet type byte = 1 byte. * - Fragment header (message_id uint16 + chunk_index uint8 + total_chunks * uint8) = 4 bytes. * Total overhead per fragment = 13 bytes; payload capacity per fragment = * 1200 - 13 = 1187 bytes. * * For a logical message that fits in a single packet (no fragmentation * needed), only the Channel header overhead applies — the payload starts * with its own packet-type byte (e.g. ACTION_STREAM) and is sent directly. * * @author Alex Goldring * @copyright Company Named Limited (c) 2025 */ /** Target transport MTU. */ export const MTU_BYTES = 1200; /** Channel layer's per-packet header size. */ export const CHANNEL_HEADER_BYTES = 8; /** * Bytes the Channel reserves for the actual payload (no fragmentation): * MTU minus the channel header. */ export const MAX_CHANNEL_PAYLOAD_BYTES = MTU_BYTES - CHANNEL_HEADER_BYTES; /** * Fragment header bytes within the channel payload: * - 1 byte for the FRAGMENT packet-type marker * - 2 bytes for message_id (uint16) * - 1 byte for chunk_index (uint8) * - 1 byte for total_chunks (uint8) */ export const FRAGMENT_HEADER_BYTES = 5; /** Maximum number of payload bytes per fragment packet. */ export const MAX_FRAGMENT_CHUNK_BYTES = MAX_CHANNEL_PAYLOAD_BYTES - FRAGMENT_HEADER_BYTES; /** Maximum number of chunks in a single logical message (limited by uint8 total_chunks). */ export const MAX_CHUNKS_PER_MESSAGE = 255; /** Largest single logical payload supportable by this fragmentation scheme. */ export const MAX_LOGICAL_MESSAGE_BYTES = MAX_FRAGMENT_CHUNK_BYTES * MAX_CHUNKS_PER_MESSAGE; /** * NACK retransmit timing. * * FRAGMENT_NACK_INITIAL_DELAY_MS — how long the receiver waits after the * first chunk of a message arrives before NACKing the missing chunks. * Trades off latency-of-recovery against premature NACKs for chunks * that were just slightly late. * FRAGMENT_NACK_RESEND_INTERVAL_MS — period between NACK retries when a * message remains incomplete. * FRAGMENT_NACK_MAX_ROUNDS — after this many NACK rounds without * completion, the receiver drops the message; the sender's retention * ages out independently. * * 75 ms initial delay is sized for the consumer mobile RTT bands measured * across US / EU / China in 2024–2025: 5G median 15–45 ms, 4G median * 30–55 ms, per-path jitter <10 ms for the majority of long-lived * connections. 75 ms sits comfortably above worst-case (4G median + jitter * + wire time for a small burst) so legitimate stragglers land before the * NACK fires, while keeping recovery latency on real loss to ~75 ms + 1 * RTT. On worst-case paths the receiver may occasionally NACK chunks * still in transit; the receiver-side dedup absorbs the duplicate * retransmits. */ export const FRAGMENT_NACK_INITIAL_DELAY_MS = 75; export const FRAGMENT_NACK_RESEND_INTERVAL_MS = 75; export const FRAGMENT_NACK_MAX_ROUNDS = 3; /** * Sender-side retention bounds. The sender keeps a copy of each fragmented * message's bytes so it can satisfy NACK retransmits. Retention is * FIFO-evicted at `FRAGMENT_RETENTION_MAX_MESSAGES` and age-evicted at * `FRAGMENT_RETENTION_MAX_AGE_MS`. * * Worst-case memory per peer: `FRAGMENT_RETENTION_MAX_MESSAGES * * max_message_size` bytes. With the FragmentAssembler default of 64 KiB, * that's 1 MiB per peer at saturation; steady-state retention is empty * because steady traffic fits in one packet and bypasses fragmentation. */ export const FRAGMENT_RETENTION_MAX_AGE_MS = 1500; export const FRAGMENT_RETENTION_MAX_MESSAGES = 16; export const FRAGMENT_RETENTION_MAX_RETRIES = 3;