murow 0.0.72 → 0.1.1

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 (115) 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/driver/driver.js +1 -1
  4. package/dist/cjs/core/driver/drivers/immediate.js +1 -1
  5. package/dist/cjs/core/driver/drivers/raf.js +1 -1
  6. package/dist/cjs/core/driver/drivers/timeout.js +1 -1
  7. package/dist/cjs/core/input/index.js +1 -1
  8. package/dist/cjs/core/input/mouse-look/index.js +1 -0
  9. package/dist/cjs/core/input/mouse-look/mouse-look.js +1 -0
  10. package/dist/cjs/core/input/scroll-zoom/index.js +1 -0
  11. package/dist/cjs/core/input/scroll-zoom/scroll-zoom.js +1 -0
  12. package/dist/cjs/core/sparse-batcher/sparse-batcher.js +1 -1
  13. package/dist/cjs/ecs/component.js +1 -1
  14. package/dist/cjs/ecs/system-builder.js +1 -1
  15. package/dist/cjs/ecs/world.js +1 -1
  16. package/dist/cjs/game/loop/loop.js +1 -1
  17. package/dist/cjs/net/adapters/bun-websocket.js +1 -1
  18. package/dist/cjs/renderer/base/renderer-3d.js +1 -1
  19. package/dist/cjs/renderer/prefab-bucket/concrete.js +1 -1
  20. package/dist/cjs/renderer/prefab-bucket/index.js +1 -1
  21. package/dist/cjs/renderer/prefab-bucket/parsers.js +1 -1
  22. package/dist/cjs/renderer/prefab-bucket/specs.js +1 -1
  23. package/dist/esm/core/binary-codec/binary-codec.js +1 -1
  24. package/dist/esm/core/driver/drivers/immediate.js +1 -1
  25. package/dist/esm/core/driver/drivers/raf.js +1 -1
  26. package/dist/esm/core/driver/drivers/timeout.js +1 -1
  27. package/dist/esm/core/input/index.js +1 -1
  28. package/dist/esm/core/input/mouse-look/index.js +1 -0
  29. package/dist/esm/core/input/mouse-look/mouse-look.js +1 -0
  30. package/dist/esm/core/input/scroll-zoom/index.js +1 -0
  31. package/dist/esm/core/input/scroll-zoom/scroll-zoom.js +1 -0
  32. package/dist/esm/core/sparse-batcher/sparse-batcher.js +1 -1
  33. package/dist/esm/ecs/component.js +1 -1
  34. package/dist/esm/ecs/system-builder.js +1 -1
  35. package/dist/esm/ecs/world.js +1 -1
  36. package/dist/esm/game/loop/loop.js +1 -1
  37. package/dist/esm/net/adapters/bun-websocket.js +1 -1
  38. package/dist/esm/renderer/base/renderer-3d.js +1 -1
  39. package/dist/esm/renderer/prefab-bucket/concrete.js +1 -1
  40. package/dist/esm/renderer/prefab-bucket/index.js +1 -1
  41. package/dist/esm/renderer/prefab-bucket/parsers.js +1 -1
  42. package/dist/netcode/cjs/index.js +1552 -0
  43. package/dist/netcode/esm/index.js +1530 -0
  44. package/dist/netcode/types/client/game-client.d.ts +125 -0
  45. package/dist/netcode/types/client/index.d.ts +1 -0
  46. package/dist/netcode/types/client/interpolation-buffer.d.ts +37 -0
  47. package/dist/netcode/types/client/interpolation-buffer.test.d.ts +1 -0
  48. package/dist/netcode/types/codec/delta-codec.d.ts +17 -0
  49. package/dist/netcode/types/codec/delta-codec.test.d.ts +1 -0
  50. package/dist/netcode/types/codec/index.d.ts +1 -0
  51. package/dist/netcode/types/components/index.d.ts +1 -0
  52. package/dist/netcode/types/components/sync-spec.d.ts +43 -0
  53. package/dist/netcode/types/components/sync-spec.test.d.ts +1 -0
  54. package/dist/netcode/types/ctx.d.ts +105 -0
  55. package/dist/netcode/types/ctx.test.d.ts +1 -0
  56. package/dist/netcode/types/handlers/define-handlers.d.ts +47 -0
  57. package/dist/netcode/types/handlers/index.d.ts +1 -0
  58. package/dist/netcode/types/index.d.ts +11 -0
  59. package/dist/netcode/types/integration.test.d.ts +1 -0
  60. package/dist/netcode/types/intents/define-intents.d.ts +53 -0
  61. package/dist/netcode/types/intents/define-intents.test.d.ts +1 -0
  62. package/dist/netcode/types/intents/index.d.ts +1 -0
  63. package/dist/netcode/types/network/base.d.ts +120 -0
  64. package/dist/netcode/types/network/index.d.ts +2 -0
  65. package/dist/netcode/types/network/transport.d.ts +1 -0
  66. package/dist/netcode/types/packets/convergence.test.d.ts +1 -0
  67. package/dist/netcode/types/packets/harness.d.ts +103 -0
  68. package/dist/netcode/types/packets/index.d.ts +2 -0
  69. package/dist/netcode/types/packets/intermittent-intents.test.d.ts +1 -0
  70. package/dist/netcode/types/packets/pathological.test.d.ts +1 -0
  71. package/dist/netcode/types/packets/peer-interpolation.test.d.ts +1 -0
  72. package/dist/netcode/types/packets/reordering.test.d.ts +1 -0
  73. package/dist/netcode/types/packets/virtual-network.d.ts +65 -0
  74. package/dist/netcode/types/predictions/define-predictions.d.ts +45 -0
  75. package/dist/netcode/types/predictions/define-predictions.test.d.ts +1 -0
  76. package/dist/netcode/types/predictions/index.d.ts +1 -0
  77. package/dist/netcode/types/reconciliation.test.d.ts +1 -0
  78. package/dist/netcode/types/rpcs/define-rpcs.d.ts +44 -0
  79. package/dist/netcode/types/rpcs/define-rpcs.test.d.ts +1 -0
  80. package/dist/netcode/types/rpcs/index.d.ts +1 -0
  81. package/dist/netcode/types/server/game-server.d.ts +77 -0
  82. package/dist/netcode/types/server/index.d.ts +2 -0
  83. package/dist/netcode/types/server/plugins/aoi-grid.d.ts +34 -0
  84. package/dist/netcode/types/server/plugins/index.d.ts +3 -0
  85. package/dist/netcode/types/server/plugins/lag-compensation.d.ts +34 -0
  86. package/dist/netcode/types/server/plugins/plugin.d.ts +24 -0
  87. package/dist/netcode/types/tick-rate.test.d.ts +1 -0
  88. package/dist/netcode/types/transports/index.d.ts +1 -0
  89. package/dist/netcode/types/transports/memory-transport.d.ts +51 -0
  90. package/dist/netcode/types/types.test.d.ts +1 -0
  91. package/dist/types/core/binary-codec/binary-codec.d.ts +89 -31
  92. package/dist/types/core/driver/driver.d.ts +8 -8
  93. package/dist/types/core/driver/drivers/immediate.d.ts +4 -4
  94. package/dist/types/core/driver/drivers/raf.d.ts +17 -6
  95. package/dist/types/core/driver/drivers/timeout.d.ts +4 -4
  96. package/dist/types/core/input/index.d.ts +2 -0
  97. package/dist/types/core/input/mouse-look/index.d.ts +1 -0
  98. package/dist/types/core/input/mouse-look/mouse-look.d.ts +139 -0
  99. package/dist/types/core/input/scroll-zoom/index.d.ts +1 -0
  100. package/dist/types/core/input/scroll-zoom/scroll-zoom.d.ts +38 -0
  101. package/dist/types/ecs/component.d.ts +67 -11
  102. package/dist/types/ecs/entity-handle.d.ts +5 -5
  103. package/dist/types/ecs/system-builder.d.ts +13 -0
  104. package/dist/types/ecs/world.d.ts +72 -4
  105. package/dist/types/game/loop/loop.d.ts +21 -2
  106. package/dist/types/net/adapters/bun-websocket.d.ts +19 -3
  107. package/dist/types/renderer/base/renderer-3d.d.ts +1 -1
  108. package/dist/types/renderer/prefab-bucket/concrete.d.ts +42 -2
  109. package/dist/types/renderer/prefab-bucket/index.d.ts +12 -2
  110. package/dist/types/renderer/prefab-bucket/specs.d.ts +46 -3
  111. package/dist/types/renderer/types.d.ts +5 -3
  112. package/dist/webgpu/cjs/index.js +591 -40
  113. package/dist/webgpu/esm/index.js +591 -40
  114. package/dist/webgpu/types/3d/renderer.d.ts +111 -5
  115. package/package.json +6 -1
@@ -2,8 +2,19 @@
2
2
  * A binary field descriptor.
3
3
  * Defines how a single value is serialized/deserialized
4
4
  * at a fixed byte size.
5
+ *
6
+ * The second type parameter `A` is a compile-time-only marker for the
7
+ * typed array kind used when this field is stored in an ECS World's
8
+ * Structure-of-Arrays column. Tagging `f32 → Float32Array`, `u8 →
9
+ * Uint8Array`, etc. lets `world.fields(Component)` return precisely
10
+ * typed Float32Array / Int32Array / ... per field without casts. The
11
+ * parameter is purely structural — it never appears at runtime.
12
+ *
13
+ * Defaults to `unknown` so existing call sites continue to compile.
14
+ * Composite fields (vec2/vec3/color/string) leave `A` defaulted; only
15
+ * the scalar primitives narrow it.
5
16
  */
6
- export type Field<T> = {
17
+ export type Field<T, A = unknown> = {
7
18
  /** Size of the field in bytes */
8
19
  size: number;
9
20
  /**
@@ -23,6 +34,13 @@ export type Field<T> = {
23
34
  * Returns the nil value
24
35
  */
25
36
  toNil(): T;
37
+ /**
38
+ * Compile-time-only phantom marker for the SoA typed array kind. Never
39
+ * read at runtime; never written by any of the primitives. Exists so
40
+ * `Field<number, Float32Array>` and `Field<number, Uint8Array>` are
41
+ * structurally distinguishable to TypeScript.
42
+ */
43
+ readonly __array?: A;
26
44
  };
27
45
  /**
28
46
  * A schema mapping object keys to binary fields.
@@ -33,8 +51,19 @@ export type Field<T> = {
33
51
  * Do not rely on computed or dynamic keys.
34
52
  */
35
53
  export type Schema<T> = {
36
- [K in keyof T]: Field<T[K]>;
54
+ [K in keyof T]: Field<T[K], any>;
37
55
  };
56
+ /**
57
+ * Helper for extracting the per-field typed-array kind from a schema.
58
+ *
59
+ * Given a precise schema literal like `{ x: BinaryCodec.f32, y: BinaryCodec.u8 }`
60
+ * (inferred narrowly, not widened to `Schema<T>`), this maps each field to
61
+ * its tagged array kind: `{ x: Float32Array, y: Uint8Array }`.
62
+ *
63
+ * Fields whose `A` is `unknown` (composites: vec/color/string) fall back
64
+ * to the broad TypedArray union.
65
+ */
66
+ export type ArrayFromField<F> = F extends Field<any, infer A> ? unknown extends A ? Float32Array | Int32Array | Uint32Array | Uint16Array | Uint8Array | Int8Array | Int16Array | Float64Array : A : never;
38
67
  /**
39
68
  * Base codec implementation.
40
69
  * Handles schema-driven encoding/decoding.
@@ -69,25 +98,25 @@ export declare class BaseBinaryCodec {
69
98
  */
70
99
  export declare class BinaryPrimitives {
71
100
  /** Unsigned 8-bit integer */
72
- static readonly u8: Field<number>;
101
+ static readonly u8: Field<number, Uint8Array>;
73
102
  /** Unsigned 16-bit integer (big-endian) */
74
- static readonly u16: Field<number>;
103
+ static readonly u16: Field<number, Uint16Array>;
75
104
  /** Unsigned 32-bit integer (big-endian) */
76
- static readonly u32: Field<number>;
105
+ static readonly u32: Field<number, Uint32Array>;
77
106
  /** Signed 8-bit integer */
78
- static readonly i8: Field<number>;
107
+ static readonly i8: Field<number, Int8Array>;
79
108
  /** Signed 16-bit integer (big-endian) */
80
- static readonly i16: Field<number>;
109
+ static readonly i16: Field<number, Int16Array>;
81
110
  /** Signed 32-bit integer (big-endian) */
82
- static readonly i32: Field<number>;
83
- /** 16-bit floating point number (IEEE 754, big-endian) */
84
- static readonly f16: Field<number>;
111
+ static readonly i32: Field<number, Int32Array>;
112
+ /** 16-bit floating point number (IEEE 754, big-endian) — stored as raw u16 bits */
113
+ static readonly f16: Field<number, Uint16Array>;
85
114
  /** 32-bit floating point number (IEEE 754, big-endian) */
86
- static readonly f32: Field<number>;
115
+ static readonly f32: Field<number, Float32Array>;
87
116
  /** 64-bit floating point number (double, big-endian) */
88
- static readonly f64: Field<number>;
117
+ static readonly f64: Field<number, Float64Array>;
89
118
  /** Boolean stored as 1 byte (0 = false, 1 = true) */
90
- static readonly bool: Field<boolean>;
119
+ static readonly bool: Field<boolean, Uint8Array>;
91
120
  /**
92
121
  * String field with UTF-8 encoding and 2-byte length prefix.
93
122
  * @param maxLength Maximum number of bytes allowed
@@ -112,17 +141,17 @@ export declare class BinaryPrimitives {
112
141
  a: number;
113
142
  }>;
114
143
  /** 32-bit floating point number (IEEE 754, little-endian) */
115
- static readonly f32_le: Field<number>;
144
+ static readonly f32_le: Field<number, Float32Array>;
116
145
  /** 64-bit floating point number (double, little-endian) */
117
- static readonly f64_le: Field<number>;
146
+ static readonly f64_le: Field<number, Float64Array>;
118
147
  /** Unsigned 16-bit integer (little-endian) */
119
- static readonly u16_le: Field<number>;
148
+ static readonly u16_le: Field<number, Uint16Array>;
120
149
  /** Unsigned 32-bit integer (little-endian) */
121
- static readonly u32_le: Field<number>;
150
+ static readonly u32_le: Field<number, Uint32Array>;
122
151
  /** Signed 16-bit integer (little-endian) */
123
- static readonly i16_le: Field<number>;
152
+ static readonly i16_le: Field<number, Int16Array>;
124
153
  /** Signed 32-bit integer (little-endian) */
125
- static readonly i32_le: Field<number>;
154
+ static readonly i32_le: Field<number, Int32Array>;
126
155
  /**
127
156
  * 2D vector of f32 stored as a tuple [x, y] (little-endian).
128
157
  * Useful for compact math data or shader-friendly layouts.
@@ -145,43 +174,43 @@ export declare class BinaryPrimitives {
145
174
  */
146
175
  export declare class BinaryCodec extends BaseBinaryCodec {
147
176
  /** Unsigned 8-bit integer field */
148
- static readonly u8: Field<number>;
177
+ static readonly u8: Field<number, Uint8Array<ArrayBufferLike>>;
149
178
  /** Unsigned 16-bit integer field */
150
- static readonly u16: Field<number>;
179
+ static readonly u16: Field<number, Uint16Array<ArrayBufferLike>>;
151
180
  /** Unsigned 32-bit integer field */
152
- static readonly u32: Field<number>;
181
+ static readonly u32: Field<number, Uint32Array<ArrayBufferLike>>;
153
182
  /** Signed 8-bit integer field */
154
- static readonly i8: Field<number>;
183
+ static readonly i8: Field<number, Int8Array<ArrayBufferLike>>;
155
184
  /** Signed 16-bit integer field */
156
- static readonly i16: Field<number>;
185
+ static readonly i16: Field<number, Int16Array<ArrayBufferLike>>;
157
186
  /** Signed 32-bit integer field */
158
- static readonly i32: Field<number>;
187
+ static readonly i32: Field<number, Int32Array<ArrayBufferLike>>;
159
188
  /** 16-bit floating point field */
160
- static readonly f16: Field<number>;
189
+ static readonly f16: Field<number, Uint16Array<ArrayBufferLike>>;
161
190
  /** 32-bit floating point field */
162
- static readonly f32: Field<number>;
191
+ static readonly f32: Field<number, Float32Array<ArrayBufferLike>>;
163
192
  /** Boolean field */
164
- static readonly bool: Field<boolean>;
193
+ static readonly bool: Field<boolean, Uint8Array<ArrayBufferLike>>;
165
194
  /** String field with length prefix */
166
195
  static string: typeof BinaryPrimitives.string;
167
196
  /** 2D vector field */
168
197
  static readonly vec2: Field<{
169
198
  x: number;
170
199
  y: number;
171
- }>;
200
+ }, unknown>;
172
201
  /** 3D vector field */
173
202
  static readonly vec3: Field<{
174
203
  x: number;
175
204
  y: number;
176
205
  z: number;
177
- }>;
206
+ }, unknown>;
178
207
  /** RGBA color field */
179
208
  static readonly color: Field<{
180
209
  r: number;
181
210
  g: number;
182
211
  b: number;
183
212
  a: number;
184
- }>;
213
+ }, unknown>;
185
214
  /**
186
215
  * Encodes an object into a binary buffer.
187
216
  */
@@ -191,3 +220,32 @@ export declare class BinaryCodec extends BaseBinaryCodec {
191
220
  */
192
221
  static decode<T extends object>(schema: Schema<T>, buf: Uint8Array, target: T): T;
193
222
  }
223
+ export declare const u8: Field<number, Uint8Array<ArrayBufferLike>>;
224
+ export declare const u16: Field<number, Uint16Array<ArrayBufferLike>>;
225
+ export declare const u32: Field<number, Uint32Array<ArrayBufferLike>>;
226
+ export declare const i8: Field<number, Int8Array<ArrayBufferLike>>;
227
+ export declare const i16: Field<number, Int16Array<ArrayBufferLike>>;
228
+ export declare const i32: Field<number, Int32Array<ArrayBufferLike>>;
229
+ export declare const f16: Field<number, Uint16Array<ArrayBufferLike>>;
230
+ export declare const f32: Field<number, Float32Array<ArrayBufferLike>>;
231
+ export declare const f64: Field<number, Float64Array<ArrayBufferLike>>;
232
+ export declare const bool: Field<boolean, Uint8Array<ArrayBufferLike>>;
233
+ export declare const string: typeof BinaryPrimitives.string;
234
+ export declare const vec2: Field<{
235
+ x: number;
236
+ y: number;
237
+ }, unknown>;
238
+ export declare const vec3: Field<{
239
+ x: number;
240
+ y: number;
241
+ z: number;
242
+ }, unknown>;
243
+ export declare const vec2_le: Field<[number, number], unknown>;
244
+ export declare const vec3_le: Field<[number, number, number], unknown>;
245
+ export declare const vec4_le: Field<[number, number, number, number], unknown>;
246
+ export declare const color: Field<{
247
+ r: number;
248
+ g: number;
249
+ b: number;
250
+ a: number;
251
+ }, unknown>;
@@ -13,7 +13,7 @@ export interface LoopDriver {
13
13
  /** Internal loop iteration method */
14
14
  loop(): void;
15
15
  /** Update callback invoked each frame with delta time in seconds */
16
- update(dt: number): void;
16
+ update(deltaTime: number): void;
17
17
  }
18
18
  /**
19
19
  * Type of driver to use for the game loop.
@@ -35,24 +35,24 @@ export type DriverType = 'server-immediate' | 'client' | 'server-timeout';
35
35
  * @example
36
36
  * ```typescript
37
37
  * // Client
38
- * const clientDriver = createDriver('client', (dt) => {
39
- * game.update(dt);
38
+ * const clientDriver = createDriver('client', (deltaTime) => {
39
+ * game.update(deltaTime);
40
40
  * renderer.render();
41
41
  * });
42
42
  * clientDriver.start();
43
43
  *
44
44
  * // Server (maximum performance)
45
- * const serverDriver = createDriver('server', (dt) => {
46
- * simulation.tick(dt);
45
+ * const serverDriver = createDriver('server', (deltaTime) => {
46
+ * simulation.tick(deltaTime);
47
47
  * });
48
48
  * serverDriver.start();
49
49
  *
50
50
  * // Server (balanced with I/O)
51
- * const balancedDriver = createDriver('server-timeout', (dt) => {
52
- * simulation.tick(dt);
51
+ * const balancedDriver = createDriver('server-timeout', (deltaTime) => {
52
+ * simulation.tick(deltaTime);
53
53
  * handleNetworkIO();
54
54
  * });
55
55
  * balancedDriver.start();
56
56
  * ```
57
57
  */
58
- export declare function createDriver(type: DriverType, update: (dt: number) => void): ImmediateDriver | RafDriver | TimeoutDriver;
58
+ export declare function createDriver(type: DriverType, update: (deltaTime: number) => void): ImmediateDriver | RafDriver | TimeoutDriver;
@@ -11,19 +11,19 @@ import { LoopDriver } from "../driver";
11
11
  *
12
12
  * @example
13
13
  * ```typescript
14
- * const driver = new ImmediateDriver((dt) => {
15
- * world.tick(dt);
14
+ * const driver = new ImmediateDriver((deltaTime) => {
15
+ * world.tick(deltaTime);
16
16
  * broadcastState();
17
17
  * });
18
18
  * driver.start();
19
19
  * ```
20
20
  */
21
21
  export declare class ImmediateDriver implements LoopDriver {
22
- update: (dt: number) => void;
22
+ update: (deltaTime: number) => void;
23
23
  /**
24
24
  * @param update - Callback invoked each tick with delta time in seconds
25
25
  */
26
- constructor(update: (dt: number) => void);
26
+ constructor(update: (deltaTime: number) => void);
27
27
  private last;
28
28
  private running;
29
29
  /**
@@ -9,26 +9,36 @@ import { LoopDriver } from "../driver";
9
9
  *
10
10
  * @example
11
11
  * ```typescript
12
- * const driver = new RafDriver((dt) => {
13
- * player.update(dt);
12
+ * const driver = new RafDriver((deltaTime) => {
13
+ * player.update(deltaTime);
14
14
  * renderer.render();
15
15
  * });
16
16
  * driver.start();
17
17
  * ```
18
18
  */
19
19
  export declare class RafDriver implements LoopDriver {
20
- update: (dt: number) => void;
20
+ update: (deltaTime: number) => void;
21
+ /**
22
+ * Hard upper bound on a single frame's delta, in ms. Long pauses (tab
23
+ * backgrounded, breakpoint hit, browser throttling) deliver one huge
24
+ * frame on resume. Clamping keeps the engine from trying to "catch up"
25
+ * by replaying that lost time at high speed.
26
+ */
27
+ private static readonly MAX_DELTA_MS;
21
28
  /**
22
29
  * @param update - Callback invoked each frame with delta time in seconds
23
30
  */
24
- constructor(update: (dt: number) => void);
31
+ constructor(update: (deltaTime: number) => void);
25
32
  private last;
26
33
  private running;
27
34
  private rafId;
35
+ private visibilityHandler;
28
36
  /**
29
37
  * Starts the game loop using requestAnimationFrame.
30
38
  *
31
- * Resets timing to prevent large initial delta.
39
+ * Resets timing to prevent large initial delta and installs a
40
+ * visibilitychange handler so the first frame after a hidden tab
41
+ * doesn't deliver a multi-second delta.
32
42
  */
33
43
  start(): void;
34
44
  /**
@@ -38,7 +48,8 @@ export declare class RafDriver implements LoopDriver {
38
48
  /**
39
49
  * Internal loop method that calculates delta time and schedules the next frame.
40
50
  *
41
- * Delta time is provided in seconds.
51
+ * Delta time is provided in seconds, clamped to `MAX_DELTA_MS` so a paused
52
+ * tab doesn't deliver a multi-second frame.
42
53
  */
43
54
  loop: () => void;
44
55
  }
@@ -16,19 +16,19 @@ import { LoopDriver } from "../driver";
16
16
  *
17
17
  * @example
18
18
  * ```typescript
19
- * const driver = new TimeoutDriver((dt) => {
20
- * world.tick(dt);
19
+ * const driver = new TimeoutDriver((deltaTime) => {
20
+ * world.tick(deltaTime);
21
21
  * broadcastState();
22
22
  * });
23
23
  * driver.start();
24
24
  * ```
25
25
  */
26
26
  export declare class TimeoutDriver implements LoopDriver {
27
- update: (dt: number) => void;
27
+ update: (deltaTime: number) => void;
28
28
  /**
29
29
  * @param update - Callback invoked each tick with delta time in seconds
30
30
  */
31
- constructor(update: (dt: number) => void);
31
+ constructor(update: (deltaTime: number) => void);
32
32
  private last;
33
33
  private running;
34
34
  /**
@@ -1,3 +1,5 @@
1
1
  export * from "./manager";
2
2
  export * from "./sources";
3
+ export * from "./mouse-look";
4
+ export * from "./scroll-zoom";
3
5
  export type { InputSnapshot } from "./types";
@@ -0,0 +1 @@
1
+ export * from "./mouse-look";
@@ -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,11 +1,17 @@
1
1
  import { Schema } from "../core/binary-codec";
2
2
  import { ArrayField } from "../core/pooled-codec";
3
3
  /**
4
- * Metadata for a component definition
4
+ * Metadata for a component definition.
5
+ *
6
+ * The second type parameter `S` carries the precise schema literal type
7
+ * (e.g. `{ x: Field<number, Float32Array>, y: Field<number, Uint8Array> }`)
8
+ * so that `world.fields(component)` can return a per-field typed-array
9
+ * map without casts. Defaults to the loose `Schema<T>` for compatibility
10
+ * with callers that don't preserve the narrow schema.
5
11
  */
6
- export interface ComponentMeta<T extends object> {
12
+ export interface ComponentMeta<T extends object, S extends Schema<T> = Schema<T>> {
7
13
  /** Schema defining the component's binary layout */
8
- schema: Schema<T>;
14
+ schema: S;
9
15
  /** Unique name for this component type */
10
16
  name: string;
11
17
  /** Size of the component in bytes */
@@ -18,33 +24,83 @@ export interface ComponentMeta<T extends object> {
18
24
  arrayCodec: ArrayField<T>;
19
25
  }
20
26
  /**
21
- * Component type returned by defineComponent
27
+ * Component type returned by defineComponent.
28
+ *
29
+ * `T` is the value-shape inferred from the schema. `S` is the precise
30
+ * schema literal type, used by `world.fields()` to return per-field
31
+ * typed-array maps with exact element types (Float32Array vs Uint8Array
32
+ * vs ...). When omitted, `S` defaults to the loose `Schema<T>` and
33
+ * `world.fields()` falls back to a broad TypedArray union per field.
22
34
  */
23
- export type Component<T extends object = any> = ComponentMeta<T> & {
35
+ export type Component<T extends object = any, S extends Schema<T> = Schema<T>> = ComponentMeta<T, S> & {
24
36
  /** Type marker for TypeScript inference */
25
37
  __type?: T;
26
38
  /** Internal: Index assigned by World when registered */
27
39
  __worldIndex?: number;
40
+ /**
41
+ * Opaque metadata attached by higher-level packages (e.g. `murow/netcode`
42
+ * stores `SyncSpec` here to mark a component as networked). Core never
43
+ * interprets this; readers narrow it to the shape they own.
44
+ */
45
+ __sync?: unknown;
28
46
  };
29
47
  /**
30
48
  * Infer the data type from a Component
31
49
  */
32
- export type InferComponentType<C> = C extends Component<infer T> ? T : never;
50
+ export type InferComponentType<C> = C extends Component<infer T, any> ? T : never;
51
+ /**
52
+ * Descriptor form of `defineComponent`. Pass `{ schema, sync }` to attach
53
+ * opaque sync metadata (consumed by `murow/netcode` to mark the component
54
+ * as networked).
55
+ */
56
+ export interface ComponentDescriptor<T extends object, S extends Schema<T> = Schema<T>> {
57
+ schema: S;
58
+ sync: unknown;
59
+ }
60
+ /**
61
+ * Helper: derive the value-shape `T` from a narrowly-typed schema literal.
62
+ * Each entry must be a `Field<T[K], any>`, and we extract the `T[K]` per key.
63
+ */
64
+ type InferSchemaShape<S> = {
65
+ [K in keyof S]: S[K] extends import("../core/binary-codec").Field<infer V, any> ? V : never;
66
+ };
33
67
  /**
34
68
  * Define a component type with its binary schema.
35
69
  *
36
- * @example
70
+ * Two call shapes are supported:
71
+ * - Bare schema (the common case): `defineComponent(name, schema)`
72
+ * - Descriptor with sync metadata: `defineComponent(name, { schema, sync })`
73
+ *
74
+ * The descriptor form attaches `__sync` to the returned component. Core
75
+ * doesn't interpret `__sync`; it's read by higher-level packages such as
76
+ * `murow/netcode`. The check for descriptor form is `'schema' in arg &&
77
+ * 'sync' in arg`, which is unambiguous because real component field names
78
+ * never collide with both keys at once.
79
+ *
80
+ * The schema literal type is preserved through inference so that
81
+ * `world.fields(component)` returns precisely-typed typed arrays per
82
+ * field (Float32Array vs Uint8Array vs Uint16Array etc.) without casts.
83
+ *
84
+ * @example Bare schema
37
85
  * ```typescript
38
86
  * const Transform = defineComponent('Transform', {
39
87
  * x: BinaryCodec.f32,
40
88
  * y: BinaryCodec.f32,
41
89
  * rotation: BinaryCodec.f32,
42
90
  * });
91
+ * ```
43
92
  *
44
- * const Health = defineComponent('Health', {
45
- * current: BinaryCodec.u16,
46
- * max: BinaryCodec.u16,
93
+ * @example Descriptor with sync
94
+ * ```typescript
95
+ * const Position = defineComponent('Position', {
96
+ * schema: { x: f32, y: f32 },
97
+ * sync: { rate: 'every-tick', interest: 'aoi' },
47
98
  * });
48
99
  * ```
49
100
  */
50
- export declare function defineComponent<T extends object>(name: string, schema: Schema<T>): Component<T>;
101
+ export declare function defineComponent<S extends Record<string, import("../core/binary-codec").Field<any, any>>>(name: string, schema: S): Component<InferSchemaShape<S> & object, S extends Schema<InferSchemaShape<S> & object> ? S : never>;
102
+ export declare function defineComponent<S extends Record<string, import("../core/binary-codec").Field<any, any>>>(name: string, def: {
103
+ schema: S;
104
+ sync: unknown;
105
+ }): Component<InferSchemaShape<S> & object, S extends Schema<InferSchemaShape<S> & object> ? S : never>;
106
+ export {};