@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
104 lines • 4.13 kB
TypeScript
/**
* Per-action-type priority accumulator for bandwidth-aware packet packing.
*
* Each action class is assigned a static priority at registration; per
* tick, each type's accumulator grows by `priority * dt`. When the
* outgoing packet's byte budget is tight, the orchestrator queries
* {@link sorted_descending} to get types in priority-ranked order and
* packs from highest down until the packet is full. Calling
* {@link consume} after a type has been packed reduces its accumulator,
* letting other types win the next tick — this is the starvation-
* resistant scheduling pattern Glenn Fiedler's *Networked Physics*
* series describes.
*
* Memory layout: two Float32Arrays indexed by `type_id` (assumed dense
* starting at 0, matching the engine's `SimActionRegistry` numbering).
* `tick` is a single pass over both arrays — cache-friendly and
* allocation-free in steady state.
*
* Scope: this module is the data-structure half. Wiring it into
* `Replicator.pack_for_peer` is the orchestrator's concern — the
* prototype's tiny action volumes make integration unnecessary, but
* a real production game with hundreds of replicated entities will
* need the Replicator to consult {@link sorted_descending} when its
* outgoing buffer approaches the MTU.
*
* @author Alex Goldring
* @copyright Company Named Limited (c) 2025
*/
export class PriorityAccumulator {
/**
* @param {{ type_count: number }} options
* `type_count` is the dense range `[0, type_count)` of action type_ids
* the accumulator will track. Should be at least one more than the
* highest registered type_id.
*/
constructor({ type_count }: {
type_count: number;
});
/** @readonly */
readonly type_count: number;
/**
* Set the static priority for a type. Typically called at action
* registration (per type, once). Larger = more important.
*
* @param {number} type_id
* @param {number} priority units are arbitrary; only relative magnitudes matter
*/
set_priority(type_id: number, priority: number): void;
/**
* @param {number} type_id
* @returns {number} the configured static priority for this type
*/
priority(type_id: number): number;
/**
* Advance every accumulator by `priority * dt`. Call once per tick
* before consulting {@link sorted_descending} / {@link consume}.
*
* @param {number} dt elapsed seconds (or any time unit consistent with
* the priority units you configured)
*/
tick(dt: number): void;
/**
* Current accumulator value for a type.
* @param {number} type_id
* @returns {number}
*/
value(type_id: number): number;
/**
* Reduce a type's accumulator by `amount`. Clamped at 0 — a type's
* accumulator never goes negative, so missing a single tick doesn't
* permanently penalize it. Call after packing an action of this type
* into an outgoing packet.
*
* @param {number} type_id
* @param {number} amount
*/
consume(type_id: number, amount: number): void;
/**
* Clear a type's accumulator to 0. Useful when an entire backlog has
* been packed (e.g. all pending actions of a type fit in one packet).
*
* @param {number} type_id
*/
reset(type_id: number): void;
/**
* Write type_ids to `out_array` in descending accumulator order. The
* orchestrator then iterates `out_array` to know which types to pack
* first.
*
* `out_array` must have capacity for `type_count` entries. Stable
* within ties (insertion order = type_id ascending), so the result
* is deterministic given the same accumulator state.
*
* @param {Int32Array|number[]} out_array
*/
sorted_descending(out_array: Int32Array | number[]): void;
/**
* Reset every accumulator to 0. Priorities are preserved. Use when
* re-establishing fresh state (e.g. after a disconnect/reconnect).
*/
clear_accumulators(): void;
#private;
}
//# sourceMappingURL=PriorityAccumulator.d.ts.map