murow 0.0.73 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +15 -1
  2. package/dist/cjs/core/binary-codec/binary-codec.js +1 -1
  3. package/dist/cjs/core/clock/clock.js +1 -0
  4. package/dist/cjs/core/clock/index.js +1 -0
  5. package/dist/cjs/core/driver/driver.js +1 -1
  6. package/dist/cjs/core/driver/drivers/immediate.js +1 -1
  7. package/dist/cjs/core/driver/drivers/raf.js +1 -1
  8. package/dist/cjs/core/driver/drivers/timeout.js +1 -1
  9. package/dist/cjs/core/hitbox/hitbox-library.js +1 -0
  10. package/dist/cjs/core/hitbox/hitbox.js +1 -0
  11. package/dist/cjs/core/hitbox/index.js +1 -0
  12. package/dist/cjs/core/hitbox/test.js +1 -0
  13. package/dist/cjs/core/index.js +1 -1
  14. package/dist/cjs/core/input/index.js +1 -1
  15. package/dist/cjs/core/input/mouse-look/index.js +1 -0
  16. package/dist/cjs/core/input/mouse-look/mouse-look.js +1 -0
  17. package/dist/cjs/core/input/scroll-zoom/index.js +1 -0
  18. package/dist/cjs/core/input/scroll-zoom/scroll-zoom.js +1 -0
  19. package/dist/cjs/core/prediction/prediction.js +1 -1
  20. package/dist/cjs/core/ray/ray-3d.js +1 -1
  21. package/dist/cjs/core/raycast/hit-buffer.js +1 -0
  22. package/dist/cjs/core/raycast/index.js +1 -0
  23. package/dist/cjs/core/raycast/raycaster.js +1 -0
  24. package/dist/cjs/core/slot-map/index.js +1 -0
  25. package/dist/cjs/core/slot-map/slot-map.js +1 -0
  26. package/dist/cjs/core/state-machine/index.js +1 -0
  27. package/dist/cjs/core/state-machine/state-machine.js +1 -0
  28. package/dist/cjs/core/timeline/index.js +1 -0
  29. package/dist/cjs/core/timeline/timeline.js +1 -0
  30. package/dist/cjs/ecs/component.js +1 -1
  31. package/dist/cjs/ecs/system-builder.js +1 -1
  32. package/dist/cjs/ecs/world.js +1 -1
  33. package/dist/cjs/game/loop/loop.js +1 -1
  34. package/dist/cjs/game/loop/ticker-schedule.js +1 -0
  35. package/dist/cjs/net/adapters/bun-websocket.js +1 -1
  36. package/dist/cjs/renderer/index.js +1 -1
  37. package/dist/cjs/renderer/prefab-bucket/concrete.js +1 -1
  38. package/dist/cjs/renderer/prefab-bucket/index.js +1 -1
  39. package/dist/cjs/renderer/prefab-bucket/parsers.js +1 -1
  40. package/dist/cjs/renderer/prefab-bucket/specs.js +1 -1
  41. package/dist/cjs/renderer/raycast/index.js +1 -0
  42. package/dist/cjs/renderer/raycast/raycast.js +1 -0
  43. package/dist/esm/core/binary-codec/binary-codec.js +1 -1
  44. package/dist/esm/core/clock/clock.js +1 -0
  45. package/dist/esm/core/clock/index.js +1 -0
  46. package/dist/esm/core/driver/drivers/immediate.js +1 -1
  47. package/dist/esm/core/driver/drivers/raf.js +1 -1
  48. package/dist/esm/core/driver/drivers/timeout.js +1 -1
  49. package/dist/esm/core/hitbox/hitbox-library.js +1 -0
  50. package/dist/esm/core/hitbox/hitbox.js +1 -0
  51. package/dist/esm/core/hitbox/index.js +1 -0
  52. package/dist/esm/core/hitbox/test.js +1 -0
  53. package/dist/esm/core/index.js +1 -1
  54. package/dist/esm/core/input/index.js +1 -1
  55. package/dist/esm/core/input/mouse-look/index.js +1 -0
  56. package/dist/esm/core/input/mouse-look/mouse-look.js +1 -0
  57. package/dist/esm/core/input/scroll-zoom/index.js +1 -0
  58. package/dist/esm/core/input/scroll-zoom/scroll-zoom.js +1 -0
  59. package/dist/esm/core/prediction/prediction.js +1 -1
  60. package/dist/esm/core/ray/ray-3d.js +1 -1
  61. package/dist/esm/core/raycast/hit-buffer.js +1 -0
  62. package/dist/esm/core/raycast/index.js +1 -0
  63. package/dist/esm/core/raycast/raycaster.js +1 -0
  64. package/dist/esm/core/slot-map/index.js +1 -0
  65. package/dist/esm/core/slot-map/slot-map.js +1 -0
  66. package/dist/esm/core/state-machine/index.js +1 -0
  67. package/dist/esm/core/state-machine/state-machine.js +1 -0
  68. package/dist/esm/core/timeline/index.js +1 -0
  69. package/dist/esm/core/timeline/timeline.js +1 -0
  70. package/dist/esm/ecs/component.js +1 -1
  71. package/dist/esm/ecs/system-builder.js +1 -1
  72. package/dist/esm/ecs/world.js +1 -1
  73. package/dist/esm/game/loop/loop.js +1 -1
  74. package/dist/esm/game/loop/ticker-schedule.js +1 -0
  75. package/dist/esm/net/adapters/bun-websocket.js +1 -1
  76. package/dist/esm/renderer/index.js +1 -1
  77. package/dist/esm/renderer/prefab-bucket/concrete.js +1 -1
  78. package/dist/esm/renderer/prefab-bucket/index.js +1 -1
  79. package/dist/esm/renderer/prefab-bucket/parsers.js +1 -1
  80. package/dist/esm/renderer/raycast/index.js +1 -0
  81. package/dist/esm/renderer/raycast/raycast.js +1 -0
  82. package/dist/netcode/cjs/index.js +1556 -0
  83. package/dist/netcode/esm/index.js +1534 -0
  84. package/dist/netcode/types/client/game-client.d.ts +139 -0
  85. package/dist/netcode/types/client/index.d.ts +1 -0
  86. package/dist/netcode/types/client/strategies/snapshot-interpolation.d.ts +33 -0
  87. package/dist/netcode/types/client/strategies/snapshot-interpolation.test.d.ts +1 -0
  88. package/dist/netcode/types/codec/delta-codec.d.ts +17 -0
  89. package/dist/netcode/types/codec/delta-codec.test.d.ts +1 -0
  90. package/dist/netcode/types/codec/index.d.ts +1 -0
  91. package/dist/netcode/types/components/index.d.ts +1 -0
  92. package/dist/netcode/types/components/sync-spec.d.ts +49 -0
  93. package/dist/netcode/types/components/sync-spec.test.d.ts +1 -0
  94. package/dist/netcode/types/ctx.d.ts +105 -0
  95. package/dist/netcode/types/ctx.test.d.ts +1 -0
  96. package/dist/netcode/types/handlers/define-handlers.d.ts +47 -0
  97. package/dist/netcode/types/handlers/index.d.ts +1 -0
  98. package/dist/netcode/types/index.d.ts +11 -0
  99. package/dist/netcode/types/integration.test.d.ts +1 -0
  100. package/dist/netcode/types/intents/define-intents.d.ts +53 -0
  101. package/dist/netcode/types/intents/define-intents.test.d.ts +1 -0
  102. package/dist/netcode/types/intents/index.d.ts +1 -0
  103. package/dist/netcode/types/network/base.d.ts +120 -0
  104. package/dist/netcode/types/network/index.d.ts +2 -0
  105. package/dist/netcode/types/network/transport.d.ts +1 -0
  106. package/dist/netcode/types/packets/convergence.test.d.ts +1 -0
  107. package/dist/netcode/types/packets/harness.d.ts +103 -0
  108. package/dist/netcode/types/packets/index.d.ts +2 -0
  109. package/dist/netcode/types/packets/intermittent-intents.test.d.ts +1 -0
  110. package/dist/netcode/types/packets/pathological.test.d.ts +1 -0
  111. package/dist/netcode/types/packets/peer-interpolation.test.d.ts +1 -0
  112. package/dist/netcode/types/packets/reordering.test.d.ts +1 -0
  113. package/dist/netcode/types/packets/virtual-network.d.ts +65 -0
  114. package/dist/netcode/types/predictions/define-predictions.d.ts +45 -0
  115. package/dist/netcode/types/predictions/define-predictions.test.d.ts +1 -0
  116. package/dist/netcode/types/predictions/index.d.ts +1 -0
  117. package/dist/netcode/types/reconciliation.test.d.ts +1 -0
  118. package/dist/netcode/types/rpcs/define-rpcs.d.ts +44 -0
  119. package/dist/netcode/types/rpcs/define-rpcs.test.d.ts +1 -0
  120. package/dist/netcode/types/rpcs/index.d.ts +1 -0
  121. package/dist/netcode/types/server/game-server.d.ts +77 -0
  122. package/dist/netcode/types/server/index.d.ts +2 -0
  123. package/dist/netcode/types/server/plugins/aoi-grid.d.ts +34 -0
  124. package/dist/netcode/types/server/plugins/index.d.ts +3 -0
  125. package/dist/netcode/types/server/plugins/lag-compensation.d.ts +34 -0
  126. package/dist/netcode/types/server/plugins/plugin.d.ts +24 -0
  127. package/dist/netcode/types/tick-rate.test.d.ts +1 -0
  128. package/dist/netcode/types/transports/index.d.ts +1 -0
  129. package/dist/netcode/types/transports/memory-transport.d.ts +51 -0
  130. package/dist/netcode/types/types.test.d.ts +1 -0
  131. package/dist/types/core/binary-codec/binary-codec.d.ts +89 -31
  132. package/dist/types/core/clock/clock.d.ts +37 -0
  133. package/dist/types/core/clock/index.d.ts +1 -0
  134. package/dist/types/core/driver/driver.d.ts +8 -8
  135. package/dist/types/core/driver/drivers/immediate.d.ts +4 -4
  136. package/dist/types/core/driver/drivers/raf.d.ts +6 -6
  137. package/dist/types/core/driver/drivers/timeout.d.ts +4 -4
  138. package/dist/types/core/hitbox/hitbox-library.d.ts +29 -0
  139. package/dist/types/core/hitbox/hitbox.d.ts +50 -0
  140. package/dist/types/core/hitbox/index.d.ts +3 -0
  141. package/dist/types/core/hitbox/test.d.ts +44 -0
  142. package/dist/types/core/index.d.ts +6 -0
  143. package/dist/types/core/input/index.d.ts +2 -0
  144. package/dist/types/core/input/mouse-look/index.d.ts +1 -0
  145. package/dist/types/core/input/mouse-look/mouse-look.d.ts +139 -0
  146. package/dist/types/core/input/scroll-zoom/index.d.ts +1 -0
  147. package/dist/types/core/input/scroll-zoom/scroll-zoom.d.ts +38 -0
  148. package/dist/types/core/prediction/prediction.d.ts +35 -58
  149. package/dist/types/core/ray/ray-3d.d.ts +21 -1
  150. package/dist/types/core/raycast/hit-buffer.d.ts +43 -0
  151. package/dist/types/core/raycast/index.d.ts +2 -0
  152. package/dist/types/core/raycast/raycaster.d.ts +54 -0
  153. package/dist/types/core/slot-map/index.d.ts +1 -0
  154. package/dist/types/core/slot-map/slot-map.d.ts +109 -0
  155. package/dist/types/core/state-machine/index.d.ts +1 -0
  156. package/dist/types/core/state-machine/state-machine.d.ts +114 -0
  157. package/dist/types/core/timeline/index.d.ts +1 -0
  158. package/dist/types/core/timeline/timeline.d.ts +34 -0
  159. package/dist/types/ecs/component.d.ts +67 -11
  160. package/dist/types/ecs/entity-handle.d.ts +5 -5
  161. package/dist/types/ecs/system-builder.d.ts +13 -0
  162. package/dist/types/ecs/world.d.ts +72 -4
  163. package/dist/types/game/loop/loop.d.ts +51 -2
  164. package/dist/types/game/loop/ticker-schedule.d.ts +52 -0
  165. package/dist/types/net/adapters/bun-websocket.d.ts +19 -3
  166. package/dist/types/renderer/index.d.ts +1 -0
  167. package/dist/types/renderer/prefab-bucket/concrete.d.ts +16 -6
  168. package/dist/types/renderer/prefab-bucket/index.d.ts +11 -7
  169. package/dist/types/renderer/prefab-bucket/specs.d.ts +10 -0
  170. package/dist/types/renderer/raycast/index.d.ts +1 -0
  171. package/dist/types/renderer/raycast/raycast.d.ts +24 -0
  172. package/dist/types/renderer/types.d.ts +1 -0
  173. package/dist/webgpu/cjs/index.js +1897 -592
  174. package/dist/webgpu/esm/index.js +1889 -578
  175. package/dist/webgpu/types/2d/raycast.d.ts +45 -0
  176. package/dist/webgpu/types/2d/renderer.d.ts +11 -0
  177. package/dist/webgpu/types/2d/sprite-accessor.d.ts +3 -1
  178. package/dist/webgpu/types/3d/hitbox.d.ts +32 -0
  179. package/dist/webgpu/types/3d/lights.d.ts +113 -0
  180. package/dist/webgpu/types/3d/lights.test.d.ts +1 -0
  181. package/dist/webgpu/types/3d/raycast.d.ts +44 -0
  182. package/dist/webgpu/types/3d/renderer.d.ts +88 -1
  183. package/dist/webgpu/types/3d/shader.d.ts +88 -5
  184. package/dist/webgpu/types/core/types.d.ts +55 -0
  185. package/dist/webgpu/types/geometry/geometry-builder.d.ts +1 -4
  186. package/dist/webgpu/types/index.d.ts +1 -0
  187. package/dist/webgpu/types/shaders/utils.d.ts +24 -0
  188. package/package.json +6 -1
@@ -0,0 +1,139 @@
1
+ import type { InputSnapshot } from "../types";
2
+ export interface AxisOptions {
3
+ /** Initial value, radians. Default 0. */
4
+ initial?: number;
5
+ /** Lower clamp, radians. Default -Infinity (unbounded). */
6
+ min?: number;
7
+ /** Upper clamp, radians. Default +Infinity (unbounded). */
8
+ max?: number;
9
+ }
10
+ export interface MouseLookOptions {
11
+ /** Radians per pixel of mouse motion. Default 0.002. */
12
+ sensitivity?: number;
13
+ /**
14
+ * Yaw axis config. Default: unbounded (yaw wraps freely around the
15
+ * vertical axis). Clamp it for cameras that don't rotate fully.
16
+ */
17
+ yaw?: AxisOptions;
18
+ /**
19
+ * Pitch axis config. Default min/max: ±PI/2 - 0.01 (just shy of
20
+ * straight up/down so the basis vectors stay well-defined).
21
+ */
22
+ pitch?: AxisOptions;
23
+ /**
24
+ * Flip horizontal look direction. With the default (false), moving
25
+ * the mouse right rotates the view right; set true to invert.
26
+ */
27
+ invertX?: boolean;
28
+ /**
29
+ * Flip vertical look direction. With the default (false), moving the
30
+ * mouse up tilts the view up; set true for flight-sim style.
31
+ */
32
+ invertY?: boolean;
33
+ /**
34
+ * Accept drag-to-look as a fallback for platforms without Pointer Lock
35
+ * (iOS Safari). When true, `update(input)` accumulates while
36
+ * `dragButton` is held even without an active lock. Default true.
37
+ */
38
+ drag?: boolean;
39
+ /** Mouse button that drives drag-to-look. Default 'left'. */
40
+ dragButton?: "left" | "middle" | "right";
41
+ }
42
+ /**
43
+ * Yaw/pitch state driven by mouse motion, with optional pointer lock and
44
+ * drag-to-look fallback.
45
+ *
46
+ * The class owns the input handling. Output helpers (`forward`, `right`,
47
+ * `up`, `orbit`) are read-only views computed from yaw/pitch each call.
48
+ *
49
+ * Zero-alloc: each output has its own backing `Float32Array(3)` reused
50
+ * across calls. Don't hold a reference past the next call to the same
51
+ * accessor; the values will overwrite. Copy if you need to persist.
52
+ *
53
+ * Usage:
54
+ * ```ts
55
+ * const look = new MouseLook({ sensitivity: 0.002 });
56
+ *
57
+ * canvas.addEventListener('click', () => {
58
+ * look.lock(canvas).catch(() => {}); // iOS: drag-to-look takes over
59
+ * });
60
+ *
61
+ * loop.events.on('tick', ({ input }) => {
62
+ * look.update(input);
63
+ *
64
+ * // FPS:
65
+ * const pos = renderer.camera.position;
66
+ * const f = look.forward;
67
+ * renderer.camera.setTarget(pos[0] + f[0], pos[1] + f[1], pos[2] + f[2]);
68
+ *
69
+ * // Or TPS / orbit:
70
+ * const c = look.orbit(playerPos, 8);
71
+ * renderer.camera.setPosition(c[0], c[1], c[2]);
72
+ * renderer.camera.setTarget(playerPos[0], playerPos[1], playerPos[2]);
73
+ * });
74
+ * ```
75
+ */
76
+ export declare class MouseLook {
77
+ yaw: number;
78
+ pitch: number;
79
+ sensitivity: number;
80
+ yawMin: number;
81
+ yawMax: number;
82
+ pitchMin: number;
83
+ pitchMax: number;
84
+ invertX: boolean;
85
+ invertY: boolean;
86
+ drag: boolean;
87
+ dragButton: "left" | "middle" | "right";
88
+ private lockedElement;
89
+ /** Active `pointerlockchange`/`error` listeners from an in-flight `lock()`. */
90
+ private pendingLockCleanup;
91
+ private _forward;
92
+ private _right;
93
+ private _up;
94
+ private _orbit;
95
+ constructor(opts?: MouseLookOptions);
96
+ /**
97
+ * Apply input deltas. Gated internally: writes happen when pointer
98
+ * lock is active, or (if `drag` is true) while `dragButton` is held.
99
+ */
100
+ update(input: InputSnapshot): void;
101
+ /**
102
+ * Request pointer lock on `element`. Resolves once locked. Rejects if
103
+ * Pointer Lock API is unavailable (iOS Safari) or the browser denies
104
+ * the request, in which case drag-to-look takes over (if enabled).
105
+ */
106
+ lock(element: HTMLElement): Promise<void>;
107
+ /** Release pointer lock if currently held. */
108
+ unlock(): void;
109
+ /** True while pointer lock is active on the element we locked to. */
110
+ get locked(): boolean;
111
+ /**
112
+ * Unit vector pointing the way the camera is facing. Shared buffer:
113
+ * don't hold the returned reference past the next `forward` read.
114
+ */
115
+ get forward(): Float32Array;
116
+ /**
117
+ * Right direction. Lies in the XZ plane, independent of pitch.
118
+ * Shared buffer: don't hold the returned reference past the next
119
+ * `right` read.
120
+ */
121
+ get right(): Float32Array;
122
+ /**
123
+ * Camera-local up. Tilts with pitch. Shared buffer: don't hold the
124
+ * returned reference past the next `up` read.
125
+ */
126
+ get up(): Float32Array;
127
+ /**
128
+ * Camera position in orbit around `target` at `distance`, given the
129
+ * current yaw/pitch. Pairs naturally with `setTarget(target)` to
130
+ * point the camera back at the orbited object. Shared buffer: don't
131
+ * hold the returned reference past the next `orbit` call.
132
+ */
133
+ orbit(target: ArrayLike<number>, distance: number): Float32Array;
134
+ /**
135
+ * Release pointer lock, drop the locked element, and detach any
136
+ * in-flight `lock()` listeners. Safe to call multiple times.
137
+ */
138
+ destroy(): void;
139
+ }
@@ -0,0 +1 @@
1
+ export * from "./scroll-zoom";
@@ -0,0 +1,38 @@
1
+ import type { InputSnapshot } from "../types";
2
+ export interface ScrollZoomOptions {
3
+ /** Starting value. */
4
+ initial: number;
5
+ /** Lower clamp. */
6
+ min: number;
7
+ /** Upper clamp. */
8
+ max: number;
9
+ /**
10
+ * Multiplied with the per-tick scroll delta. Positive scroll (wheel
11
+ * forward / two-finger up) increases `value` by `sensitivity *
12
+ * deltaScrollY`. Use a negative sensitivity to invert. Default 0.01.
13
+ */
14
+ sensitivity?: number;
15
+ }
16
+ /**
17
+ * Scroll-wheel driven scalar with clamps. Use for orbit distance, FOV,
18
+ * RTS camera height, anything that's "scroll to change a number."
19
+ *
20
+ * Usage:
21
+ * ```ts
22
+ * const zoom = new ScrollZoom({ initial: 8, min: 3, max: 20 });
23
+ *
24
+ * loop.events.on('tick', ({ input }) => {
25
+ * zoom.update(input);
26
+ * const [cx, cy, cz] = mouseLook.orbit(playerPos, zoom.value);
27
+ * // ...
28
+ * });
29
+ * ```
30
+ */
31
+ export declare class ScrollZoom {
32
+ value: number;
33
+ min: number;
34
+ max: number;
35
+ sensitivity: number;
36
+ constructor(opts: ScrollZoomOptions);
37
+ update(input: InputSnapshot): void;
38
+ }
@@ -1,65 +1,42 @@
1
1
  /**
2
- * @template T
3
- * @description
4
- * Tracks client-side intents that have been sent to the server but not yet confirmed.
5
- * Used for prediction and reconciliation in a server-authoritative architecture.
2
+ * Bounded, sequence-ordered log of locally-applied commands awaiting
3
+ * confirmation. Records are pushed in ascending sequence; `dropThrough`
4
+ * removes everything the authority has confirmed.
6
5
  */
7
- export declare class IntentTracker<T> {
8
- tracker: Map<number, T[]>;
6
+ export declare class PredictionLog<Cmd> {
7
+ private capacity;
8
+ private entries;
9
+ constructor(capacity?: number);
9
10
  get size(): number;
10
- /**
11
- * Adds a new intent for a specific tick.
12
- * @param {number} tick - The tick number associated with the intent.
13
- * @param {T} intent - The intent data.
14
- */
15
- track(tick: number, intent: T): T;
16
- /**
17
- * Removes all intents up to and including a given tick.
18
- * Returns the remaining intents in ascending tick order.
19
- * @param {number} tick - The tick up to which intents should be dropped.
20
- * @returns {T[]} Array of remaining intents.
21
- */
22
- dropUpTo(tick: number): T[];
23
- /**
24
- * Returns all currently tracked intents in ascending tick order.
25
- * @returns {T[]}
26
- */
27
- values(): T[];
11
+ /** Record a command with its sequence. Commands must be recorded in ascending sequence. */
12
+ record(sequence: number, cmd: Cmd): void;
13
+ /** Drop every entry with sequence <= the confirmed sequence. */
14
+ dropThrough(sequence: number): void;
15
+ /** The commands still awaiting confirmation, in order. */
16
+ pending(): Cmd[];
17
+ clear(): void;
18
+ }
19
+ export interface ReconcilerOptions<Cmd, Ctx> {
20
+ /** Max unconfirmed commands kept; older ones are dropped. Default 64. */
21
+ bufferSize?: number;
22
+ /** Load authoritative state. Runs before confirmed commands are dropped. */
23
+ restore: (ctx: Ctx) => void;
24
+ /** Re-apply the still-unconfirmed commands on top of the restored state. */
25
+ replay: (cmds: Cmd[], ctx: Ctx) => void;
28
26
  }
29
27
  /**
30
- * @template T,U
31
- * @description
32
- * Handles client-side reconciliation of authoritative snapshots with unconfirmed intents.
33
- * Used for prediction correction in server-authoritative multiplayer games.
28
+ * Client-side rollback-replay: record locally-applied commands, and when the
29
+ * authority confirms up to a sequence, restore its state, drop the confirmed
30
+ * commands, and replay the rest. Domain-agnostic; the caller supplies what a
31
+ * command is, how to restore, and how to replay via callbacks.
34
32
  */
35
- export declare class Reconciliator<T, U> {
36
- private options;
37
- tracker: IntentTracker<T>;
38
- /**
39
- * @param {Object} options - Callbacks for applying snapshot state and replaying intents.
40
- * @param {(snapshotState: U) => void} options.onLoadState - Called to load authoritative snapshot state.
41
- * @param {(remainingIntents: T[]) => void} options.onReplay - Called to reapply remaining intents for prediction.
42
- */
43
- constructor(options: {
44
- onLoadState: (snapshotState: U) => void;
45
- onReplay: (remainingIntents: T[]) => void;
46
- });
47
- /**
48
- * Adds a new intent to the tracker.
49
- * @param {number} tick - Tick number associated with the intent.
50
- * @param {T} intent - The intent data.
51
- */
52
- trackIntent(tick: number, intent: T): void;
53
- /**
54
- * Called when an authoritative snapshot is received from the server.
55
- * Resets client state and replays unconfirmed intents.
56
- * @param {Object} snapshot - The snapshot from the server.
57
- * @param {number} snapshot.tick - Tick number of the snapshot.
58
- * @param {U} snapshot.state - The authoritative state.
59
- */
60
- onSnapshot(snapshot: {
61
- tick: number;
62
- state: U;
63
- }): void;
64
- replay(intents: T[]): void;
33
+ export declare class Reconciler<Cmd, Ctx = void> {
34
+ private log;
35
+ private restore;
36
+ private replay;
37
+ constructor(opts: ReconcilerOptions<Cmd, Ctx>);
38
+ record(sequence: number, cmd: Cmd): void;
39
+ get pending(): number;
40
+ clear(): void;
41
+ reconcile(ackSequence: number, ctx: Ctx): void;
65
42
  }
@@ -35,7 +35,27 @@ export declare class Ray3D {
35
35
  */
36
36
  intersectsAABB(minX: number, minY: number, minZ: number, maxX: number, maxY: number, maxZ: number): number | null;
37
37
  /**
38
- * Intersection with a triangle using the Möller–Trumbore algorithm.
38
+ * Sphere entry for picking: `t` to the nearest front-facing surface,
39
+ * or `null`. Unlike `intersectsSphere`, an origin inside the sphere or
40
+ * a sphere entirely behind the origin return `null` -- you only pick
41
+ * surfaces facing you.
42
+ */
43
+ entrySphere(cx: number, cy: number, cz: number, r: number): number | null;
44
+ /**
45
+ * Axis-aligned box entry for picking, given center and half-extents.
46
+ * `t` to the front face, or `null`. Origin-inside and fully-behind
47
+ * both return `null` (see `entrySphere`).
48
+ */
49
+ entryBox(cx: number, cy: number, cz: number, hx: number, hy: number, hz: number): number | null;
50
+ /**
51
+ * Y-axis-aligned cylinder entry for picking: center `(cx,cy,cz)`,
52
+ * radius `r`, total height `h` (spans `cy +- h/2`). Side = 2D
53
+ * ray-vs-circle in XZ keeping in-range Y; caps = the two disk planes.
54
+ * Nearest candidate, or `null`; origin-inside is rejected.
55
+ */
56
+ entryCylinder(cx: number, cy: number, cz: number, r: number, h: number): number | null;
57
+ /**
58
+ * Intersection with a triangle using the Möller-Trumbore algorithm.
39
59
  * Returns `t` at the hit point, `null` if no hit.
40
60
  */
41
61
  intersectsTriangle(ax: number, ay: number, az: number, bx: number, by: number, bz: number, cx: number, cy: number, cz: number): number | null;
@@ -0,0 +1,43 @@
1
+ export interface BufferedHit<H, Point extends number[]> {
2
+ handle: H;
3
+ distance: number;
4
+ point: Point;
5
+ /** Name of the hitbox part struck, or `null` for a default-bound (sphere/box/quad) hit. */
6
+ part: string | null;
7
+ }
8
+ type Filter<H> = (handle: H) => boolean;
9
+ export declare class HitBuffer<H, Point extends number[]> {
10
+ private handles;
11
+ private parts;
12
+ private keys;
13
+ private distances;
14
+ private px;
15
+ private py;
16
+ private pz;
17
+ private order;
18
+ count: number;
19
+ private capacity;
20
+ private sorted;
21
+ private readonly dims;
22
+ private nearestHit;
23
+ constructor(dims: 2 | 3);
24
+ private makeHit;
25
+ reset(): void;
26
+ /**
27
+ * `key` orders the hit (ascending = nearer); `distance` is the value
28
+ * the public hit reports. They differ only when ordering isn't by
29
+ * distance (e.g. 2D layer). `distance` defaults to `key`.
30
+ */
31
+ push(handle: H, key: number, x: number, y: number, z?: number, distance?: number, part?: string | null): void;
32
+ nearest(filter: Filter<H> | undefined, cap: number): BufferedHit<H, Point> | null;
33
+ collectInto(out: BufferedHit<H, Point>[], filter: Filter<H> | undefined, cap: number): void;
34
+ /**
35
+ * True if any live hit's handle matches `id` — comparing the handle
36
+ * directly when it is a number, or its `.id` when it is an object.
37
+ */
38
+ containsId(id: number): boolean;
39
+ private fill;
40
+ private grow;
41
+ private ensureSorted;
42
+ }
43
+ export {};
@@ -0,0 +1,2 @@
1
+ export * from './hit-buffer';
2
+ export * from './raycaster';
@@ -0,0 +1,54 @@
1
+ import type { Ray3D } from '../ray/ray-3d';
2
+ import type { Hitbox } from '../hitbox/hitbox';
3
+ import { type BufferedHit } from './hit-buffer';
4
+ /** Per-axis column accessors. Each returns the field arrays for a frame's worth of entities. */
5
+ interface Transform3D {
6
+ position: () => {
7
+ x: ArrayLike<number>;
8
+ y: ArrayLike<number>;
9
+ z: ArrayLike<number>;
10
+ };
11
+ scale: () => {
12
+ x: ArrayLike<number>;
13
+ y: ArrayLike<number>;
14
+ z: ArrayLike<number>;
15
+ };
16
+ }
17
+ interface Lookup {
18
+ /** Candidate ids to test this cast (e.g. an ECS query result). */
19
+ query: () => readonly number[];
20
+ /** Resolve an id to its hitbox, or `null` to skip it. */
21
+ hitbox: (id: number) => Hitbox<'3d'> | null;
22
+ }
23
+ type Hit = BufferedHit<number, [number, number, number]>;
24
+ interface QueryOptions {
25
+ filter?: (id: number) => boolean;
26
+ maxDistance?: number;
27
+ }
28
+ /**
29
+ * Raycaster — casts a ray against a set of entities and ranks the hits.
30
+ *
31
+ * Source-agnostic: `lookup` supplies the candidate ids and their hitboxes,
32
+ * `configure` supplies how to read each id's transform. Both carry all
33
+ * world knowledge as closures, so the raycaster depends on neither an ECS
34
+ * nor a renderer. Owns a reused hit buffer; `cast` is allocation-free per
35
+ * entity. The same instance can be exported from shared code and wired to
36
+ * a sim world on the server and a render world on the client.
37
+ */
38
+ export declare class Raycaster {
39
+ private buf;
40
+ private resultBuffer;
41
+ private _lookup;
42
+ private _transform;
43
+ /** Wire the candidate source: which ids to test and how to resolve each id's hitbox. */
44
+ lookup(lookup: Lookup): this;
45
+ /** Wire the transform source: how to read each id's position and scale. */
46
+ configure(transform: Transform3D): this;
47
+ /** Cast `ray` against the configured source, populating the hit buffer. Chains to `hit`/`hitAll`. */
48
+ cast(ray: Ray3D): this;
49
+ /** Nearest hit, or null. Pool-backed; valid until the next cast. */
50
+ hit(opts?: QueryOptions): Hit | null;
51
+ /** All hits, nearest first. Reused array; do not retain across casts. */
52
+ hitAll(opts?: QueryOptions): readonly Hit[];
53
+ }
54
+ export {};
@@ -0,0 +1 @@
1
+ export * from "./slot-map";
@@ -0,0 +1,109 @@
1
+ /**
2
+ * A branded integer id. Use `as SlotId<'light'>` (or your own brand) to keep
3
+ * a light id from being passed where an instance id is expected. Purely a
4
+ * compile-time tag — at runtime it is a plain number.
5
+ */
6
+ export type SlotId<Brand extends string = string> = number & {
7
+ readonly __slot?: Brand;
8
+ };
9
+ /**
10
+ * Dense slot set. Slots are allocated from a fixed-capacity FreeList; the id
11
+ * returned by `add` IS the slot and stays stable until freed. Live slots are
12
+ * kept in a packed array (`activeSlots`) for cache-friendly iteration with no
13
+ * per-call allocation, and a sparse `Int32Array` maps slot -> dense position
14
+ * (`-1` when absent) for O(1) membership and removal.
15
+ */
16
+ export declare class SlotMap {
17
+ private readonly freeList;
18
+ /** Packed live slots, valid for `[0, size)`. Iterate this. */
19
+ private readonly dense;
20
+ /** slot -> index into `dense`, or `-1` if the slot is not live. */
21
+ private readonly sparse;
22
+ private _size;
23
+ private readonly _capacity;
24
+ constructor(capacity: number);
25
+ /**
26
+ * Allocate a slot and add it to the live set.
27
+ * @returns the slot, or `-1` if the pool is exhausted.
28
+ */
29
+ add(): number;
30
+ /**
31
+ * Remove a slot from the live set and return it to the pool. Keeps `dense`
32
+ * packed by swapping the last live slot into the freed position and fixing
33
+ * its sparse entry. No-op if the slot is not live.
34
+ */
35
+ remove(slot: number): void;
36
+ /** Whether `slot` is currently live. O(1). */
37
+ has(slot: number): boolean;
38
+ /**
39
+ * The packed live-slot array. Valid for indices `[0, size)`; entries past
40
+ * `size` are stale. Reused across calls — do not retain.
41
+ */
42
+ get activeSlots(): Uint32Array;
43
+ /** Number of live slots. Iterate `activeSlots` over `[0, size)`. */
44
+ get size(): number;
45
+ /** Configured capacity (max simultaneously live slots). */
46
+ get capacity(): number;
47
+ /** Whether another slot can be allocated. */
48
+ hasAvailable(): boolean;
49
+ /**
50
+ * Iterate live slots in packed order. Zero allocations. Removing the
51
+ * current slot inside the callback is safe (swap-and-pop), but the
52
+ * swapped-in slot then occupies the current index — guard accordingly or
53
+ * iterate `activeSlots` manually if you need full control.
54
+ */
55
+ forEach(fn: (slot: number, index: number) => void): void;
56
+ /** Empty the set, returning every slot to the pool. */
57
+ clear(): void;
58
+ }
59
+ /**
60
+ * Dense object set keyed by an external id. Wraps a SlotMap (slot lifecycle +
61
+ * dense iteration) and a slot-indexed object array, plus a sparse id -> slot
62
+ * map so the external id need not equal the slot. The typed, tested
63
+ * replacement for `Map<id, T>` collections you iterate every frame.
64
+ *
65
+ * `capacity` caps how many items can be live at once. `maxId` sizes the id
66
+ * lookup table and defaults to `capacity`; pass it when ids come from a larger
67
+ * space than the store holds (e.g. a 256-slot archetype store keyed by ECS
68
+ * entity ids drawn from `maxEntities`). Ids must be non-negative integers
69
+ * below `maxId`. If your identity is a string or otherwise non-integer, keep a
70
+ * `Map<string, number>` at the boundary that translates it to a dense id once.
71
+ */
72
+ export declare class SlotStore<TId extends number, T> {
73
+ private readonly slots;
74
+ /** slot -> stored item. */
75
+ private readonly items;
76
+ /** external id -> slot, or `-1` if the id is not present. Sized by maxId. */
77
+ private readonly idToSlot;
78
+ /** slot -> external id (so dense iteration can report the id). */
79
+ private readonly slotToId;
80
+ private readonly _maxId;
81
+ constructor(capacity: number, maxId?: number);
82
+ /**
83
+ * Store `item` under `id`. Allocates a slot. Throws if `id` is already
84
+ * present or the pool is exhausted.
85
+ * @returns the allocated slot.
86
+ */
87
+ add(id: TId, item: T): number;
88
+ /** Remove the item stored under `id`. No-op if absent or out of range. */
89
+ remove(id: TId): void;
90
+ /** The item stored under `id`, or `null` if absent or out of range. O(1). */
91
+ get(id: TId): T | null;
92
+ /** Whether `id` is present. O(1). */
93
+ has(id: TId): boolean;
94
+ /** The stable slot for `id`, or `-1` if absent or out of range. */
95
+ slotOf(id: TId): number;
96
+ /** Number of stored items. */
97
+ get size(): number;
98
+ /** Configured capacity. */
99
+ get capacity(): number;
100
+ /** Whether another item can be added. */
101
+ hasAvailable(): boolean;
102
+ /**
103
+ * Iterate stored items in packed order. Zero allocations. The callback
104
+ * receives the item, its external id, and its slot.
105
+ */
106
+ forEach(fn: (item: T, id: TId, slot: number) => void): void;
107
+ /** Remove every item. */
108
+ clear(): void;
109
+ }
@@ -0,0 +1 @@
1
+ export * from "./state-machine";
@@ -0,0 +1,114 @@
1
+ import { EventSystem } from "../events";
2
+ import { SimpleRNG } from "../simple-rng";
3
+ import type { Field } from "../binary-codec";
4
+ type Schema = Record<string, Field<any>>;
5
+ type StatesSpec = Record<string, Schema>;
6
+ type FieldValue<F> = F extends Field<infer T, any> ? T : never;
7
+ type SchemaValues<Sc extends Schema> = {
8
+ -readonly [K in keyof Sc]: FieldValue<Sc[K]>;
9
+ };
10
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
11
+ type StateFields<S extends StatesSpec> = UnionToIntersection<{
12
+ [K in keyof S]: SchemaValues<S[K]>;
13
+ }[keyof S]>;
14
+ /**
15
+ * Per-entity cursor over the machine's columns. Reused for the entity's
16
+ * lifetime; reading a field returns that entity's value, writing sets it.
17
+ */
18
+ export type Handle<S extends StatesSpec> = HandleBase<S> & StateFields<S>;
19
+ interface HandleBase<S extends StatesSpec> {
20
+ readonly id: number;
21
+ readonly state: keyof S & string;
22
+ readonly stateId: number;
23
+ readonly ticksInState: number;
24
+ is(state: keyof S & string): boolean;
25
+ change(to: (keyof S & string) | number, payload?: number): void;
26
+ }
27
+ interface Handlers<H> {
28
+ enter?(handle: H, payload: number): void;
29
+ update?(handle: H): void;
30
+ exit?(handle: H): void;
31
+ }
32
+ interface ChangeEvent {
33
+ id: number;
34
+ from: number;
35
+ to: number;
36
+ }
37
+ type ChangeEvents = [["change", ChangeEvent]];
38
+ interface StateMachineOptions<S extends StatesSpec> {
39
+ initial: keyof S & string;
40
+ states: S;
41
+ capacity?: number;
42
+ maxId?: number;
43
+ rng?: SimpleRNG;
44
+ }
45
+ /**
46
+ * Fixed-capacity, zero-GC state machine over many entities. State and
47
+ * per-state data live in binary columns indexed by a stable slot; one machine
48
+ * definition drives every entity. Behavior is registered per state with `add`
49
+ * and runs during `tick`. Depends only on `SlotStore`, `EventSystem`,
50
+ * `SimpleRNG`, and `BinaryCodec` fields.
51
+ */
52
+ export declare class StateMachine<S extends StatesSpec> {
53
+ /** State name to numeric id. */
54
+ readonly id: {
55
+ readonly [K in keyof S & string]: number;
56
+ };
57
+ readonly rng: SimpleRNG;
58
+ /** Transition channel, emitted whenever any entity changes state. */
59
+ readonly events: EventSystem<ChangeEvents>;
60
+ private readonly names;
61
+ private readonly initialId;
62
+ private readonly store;
63
+ private readonly stateCol;
64
+ private readonly prevCol;
65
+ private readonly enteredCol;
66
+ private readonly cols;
67
+ private readonly fieldsByState;
68
+ private readonly enterFns;
69
+ private readonly updateFns;
70
+ private readonly exitFns;
71
+ private readonly Handle;
72
+ private now;
73
+ private ticking;
74
+ private readonly dead;
75
+ private readonly ev;
76
+ constructor(opts: StateMachineOptions<S>);
77
+ /** Number of live entities. */
78
+ get size(): number;
79
+ /**
80
+ * Register behavior for a state. Cumulative: calling `add` again for the
81
+ * same state appends another set of handlers. Returns the machine for
82
+ * chaining.
83
+ */
84
+ add(state: keyof S & string, handlers: Handlers<Handle<S>>): this;
85
+ /** Register an entity by id, in the initial state. Returns its handle. */
86
+ spawn(id: number): Handle<S>;
87
+ /** The handle for `id`, or `null` if absent. */
88
+ of(id: number): Handle<S> | null;
89
+ has(id: number): boolean;
90
+ /** Remove an entity. Deferred to the end of the pass if called during `tick`. */
91
+ remove(id: number): void;
92
+ /**
93
+ * Advance every entity by one step. Runs the current state's `update`
94
+ * handlers in registration order; the first that transitions ends that
95
+ * entity's chain for this step.
96
+ */
97
+ tick(): void;
98
+ /**
99
+ * Write an entity's state id and current-state fields into `dv` at
100
+ * `offset`. Returns the offset past the written bytes.
101
+ */
102
+ serialize(id: number, dv: DataView, offset: number): number;
103
+ /**
104
+ * Load an entity's state id and fields from `dv` at `offset`, re-anchoring
105
+ * its `ticksInState`. Returns the offset past the read bytes.
106
+ */
107
+ restore(id: number, dv: DataView, offset: number): number;
108
+ /** Serialized byte size of an entity in its current state. */
109
+ byteSize(id: number): number;
110
+ private readonly step;
111
+ private transition;
112
+ private buildHandle;
113
+ }
114
+ export {};
@@ -0,0 +1 @@
1
+ export { Timeline, type TimelineEntry } from './timeline';