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.
- package/README.md +15 -1
- package/dist/cjs/core/binary-codec/binary-codec.js +1 -1
- package/dist/cjs/core/clock/clock.js +1 -0
- package/dist/cjs/core/clock/index.js +1 -0
- package/dist/cjs/core/driver/driver.js +1 -1
- package/dist/cjs/core/driver/drivers/immediate.js +1 -1
- package/dist/cjs/core/driver/drivers/raf.js +1 -1
- package/dist/cjs/core/driver/drivers/timeout.js +1 -1
- package/dist/cjs/core/hitbox/hitbox-library.js +1 -0
- package/dist/cjs/core/hitbox/hitbox.js +1 -0
- package/dist/cjs/core/hitbox/index.js +1 -0
- package/dist/cjs/core/hitbox/test.js +1 -0
- package/dist/cjs/core/index.js +1 -1
- package/dist/cjs/core/input/index.js +1 -1
- package/dist/cjs/core/input/mouse-look/index.js +1 -0
- package/dist/cjs/core/input/mouse-look/mouse-look.js +1 -0
- package/dist/cjs/core/input/scroll-zoom/index.js +1 -0
- package/dist/cjs/core/input/scroll-zoom/scroll-zoom.js +1 -0
- package/dist/cjs/core/prediction/prediction.js +1 -1
- package/dist/cjs/core/ray/ray-3d.js +1 -1
- package/dist/cjs/core/raycast/hit-buffer.js +1 -0
- package/dist/cjs/core/raycast/index.js +1 -0
- package/dist/cjs/core/raycast/raycaster.js +1 -0
- package/dist/cjs/core/slot-map/index.js +1 -0
- package/dist/cjs/core/slot-map/slot-map.js +1 -0
- package/dist/cjs/core/state-machine/index.js +1 -0
- package/dist/cjs/core/state-machine/state-machine.js +1 -0
- package/dist/cjs/core/timeline/index.js +1 -0
- package/dist/cjs/core/timeline/timeline.js +1 -0
- package/dist/cjs/ecs/component.js +1 -1
- package/dist/cjs/ecs/system-builder.js +1 -1
- package/dist/cjs/ecs/world.js +1 -1
- package/dist/cjs/game/loop/loop.js +1 -1
- package/dist/cjs/game/loop/ticker-schedule.js +1 -0
- package/dist/cjs/net/adapters/bun-websocket.js +1 -1
- package/dist/cjs/renderer/index.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/concrete.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/index.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/parsers.js +1 -1
- package/dist/cjs/renderer/prefab-bucket/specs.js +1 -1
- package/dist/cjs/renderer/raycast/index.js +1 -0
- package/dist/cjs/renderer/raycast/raycast.js +1 -0
- package/dist/esm/core/binary-codec/binary-codec.js +1 -1
- package/dist/esm/core/clock/clock.js +1 -0
- package/dist/esm/core/clock/index.js +1 -0
- package/dist/esm/core/driver/drivers/immediate.js +1 -1
- package/dist/esm/core/driver/drivers/raf.js +1 -1
- package/dist/esm/core/driver/drivers/timeout.js +1 -1
- package/dist/esm/core/hitbox/hitbox-library.js +1 -0
- package/dist/esm/core/hitbox/hitbox.js +1 -0
- package/dist/esm/core/hitbox/index.js +1 -0
- package/dist/esm/core/hitbox/test.js +1 -0
- package/dist/esm/core/index.js +1 -1
- package/dist/esm/core/input/index.js +1 -1
- package/dist/esm/core/input/mouse-look/index.js +1 -0
- package/dist/esm/core/input/mouse-look/mouse-look.js +1 -0
- package/dist/esm/core/input/scroll-zoom/index.js +1 -0
- package/dist/esm/core/input/scroll-zoom/scroll-zoom.js +1 -0
- package/dist/esm/core/prediction/prediction.js +1 -1
- package/dist/esm/core/ray/ray-3d.js +1 -1
- package/dist/esm/core/raycast/hit-buffer.js +1 -0
- package/dist/esm/core/raycast/index.js +1 -0
- package/dist/esm/core/raycast/raycaster.js +1 -0
- package/dist/esm/core/slot-map/index.js +1 -0
- package/dist/esm/core/slot-map/slot-map.js +1 -0
- package/dist/esm/core/state-machine/index.js +1 -0
- package/dist/esm/core/state-machine/state-machine.js +1 -0
- package/dist/esm/core/timeline/index.js +1 -0
- package/dist/esm/core/timeline/timeline.js +1 -0
- package/dist/esm/ecs/component.js +1 -1
- package/dist/esm/ecs/system-builder.js +1 -1
- package/dist/esm/ecs/world.js +1 -1
- package/dist/esm/game/loop/loop.js +1 -1
- package/dist/esm/game/loop/ticker-schedule.js +1 -0
- package/dist/esm/net/adapters/bun-websocket.js +1 -1
- package/dist/esm/renderer/index.js +1 -1
- package/dist/esm/renderer/prefab-bucket/concrete.js +1 -1
- package/dist/esm/renderer/prefab-bucket/index.js +1 -1
- package/dist/esm/renderer/prefab-bucket/parsers.js +1 -1
- package/dist/esm/renderer/raycast/index.js +1 -0
- package/dist/esm/renderer/raycast/raycast.js +1 -0
- package/dist/netcode/cjs/index.js +1556 -0
- package/dist/netcode/esm/index.js +1534 -0
- package/dist/netcode/types/client/game-client.d.ts +139 -0
- package/dist/netcode/types/client/index.d.ts +1 -0
- package/dist/netcode/types/client/strategies/snapshot-interpolation.d.ts +33 -0
- package/dist/netcode/types/client/strategies/snapshot-interpolation.test.d.ts +1 -0
- package/dist/netcode/types/codec/delta-codec.d.ts +17 -0
- package/dist/netcode/types/codec/delta-codec.test.d.ts +1 -0
- package/dist/netcode/types/codec/index.d.ts +1 -0
- package/dist/netcode/types/components/index.d.ts +1 -0
- package/dist/netcode/types/components/sync-spec.d.ts +49 -0
- package/dist/netcode/types/components/sync-spec.test.d.ts +1 -0
- package/dist/netcode/types/ctx.d.ts +105 -0
- package/dist/netcode/types/ctx.test.d.ts +1 -0
- package/dist/netcode/types/handlers/define-handlers.d.ts +47 -0
- package/dist/netcode/types/handlers/index.d.ts +1 -0
- package/dist/netcode/types/index.d.ts +11 -0
- package/dist/netcode/types/integration.test.d.ts +1 -0
- package/dist/netcode/types/intents/define-intents.d.ts +53 -0
- package/dist/netcode/types/intents/define-intents.test.d.ts +1 -0
- package/dist/netcode/types/intents/index.d.ts +1 -0
- package/dist/netcode/types/network/base.d.ts +120 -0
- package/dist/netcode/types/network/index.d.ts +2 -0
- package/dist/netcode/types/network/transport.d.ts +1 -0
- package/dist/netcode/types/packets/convergence.test.d.ts +1 -0
- package/dist/netcode/types/packets/harness.d.ts +103 -0
- package/dist/netcode/types/packets/index.d.ts +2 -0
- package/dist/netcode/types/packets/intermittent-intents.test.d.ts +1 -0
- package/dist/netcode/types/packets/pathological.test.d.ts +1 -0
- package/dist/netcode/types/packets/peer-interpolation.test.d.ts +1 -0
- package/dist/netcode/types/packets/reordering.test.d.ts +1 -0
- package/dist/netcode/types/packets/virtual-network.d.ts +65 -0
- package/dist/netcode/types/predictions/define-predictions.d.ts +45 -0
- package/dist/netcode/types/predictions/define-predictions.test.d.ts +1 -0
- package/dist/netcode/types/predictions/index.d.ts +1 -0
- package/dist/netcode/types/reconciliation.test.d.ts +1 -0
- package/dist/netcode/types/rpcs/define-rpcs.d.ts +44 -0
- package/dist/netcode/types/rpcs/define-rpcs.test.d.ts +1 -0
- package/dist/netcode/types/rpcs/index.d.ts +1 -0
- package/dist/netcode/types/server/game-server.d.ts +77 -0
- package/dist/netcode/types/server/index.d.ts +2 -0
- package/dist/netcode/types/server/plugins/aoi-grid.d.ts +34 -0
- package/dist/netcode/types/server/plugins/index.d.ts +3 -0
- package/dist/netcode/types/server/plugins/lag-compensation.d.ts +34 -0
- package/dist/netcode/types/server/plugins/plugin.d.ts +24 -0
- package/dist/netcode/types/tick-rate.test.d.ts +1 -0
- package/dist/netcode/types/transports/index.d.ts +1 -0
- package/dist/netcode/types/transports/memory-transport.d.ts +51 -0
- package/dist/netcode/types/types.test.d.ts +1 -0
- package/dist/types/core/binary-codec/binary-codec.d.ts +89 -31
- package/dist/types/core/clock/clock.d.ts +37 -0
- package/dist/types/core/clock/index.d.ts +1 -0
- package/dist/types/core/driver/driver.d.ts +8 -8
- package/dist/types/core/driver/drivers/immediate.d.ts +4 -4
- package/dist/types/core/driver/drivers/raf.d.ts +6 -6
- package/dist/types/core/driver/drivers/timeout.d.ts +4 -4
- package/dist/types/core/hitbox/hitbox-library.d.ts +29 -0
- package/dist/types/core/hitbox/hitbox.d.ts +50 -0
- package/dist/types/core/hitbox/index.d.ts +3 -0
- package/dist/types/core/hitbox/test.d.ts +44 -0
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/core/input/index.d.ts +2 -0
- package/dist/types/core/input/mouse-look/index.d.ts +1 -0
- package/dist/types/core/input/mouse-look/mouse-look.d.ts +139 -0
- package/dist/types/core/input/scroll-zoom/index.d.ts +1 -0
- package/dist/types/core/input/scroll-zoom/scroll-zoom.d.ts +38 -0
- package/dist/types/core/prediction/prediction.d.ts +35 -58
- package/dist/types/core/ray/ray-3d.d.ts +21 -1
- package/dist/types/core/raycast/hit-buffer.d.ts +43 -0
- package/dist/types/core/raycast/index.d.ts +2 -0
- package/dist/types/core/raycast/raycaster.d.ts +54 -0
- package/dist/types/core/slot-map/index.d.ts +1 -0
- package/dist/types/core/slot-map/slot-map.d.ts +109 -0
- package/dist/types/core/state-machine/index.d.ts +1 -0
- package/dist/types/core/state-machine/state-machine.d.ts +114 -0
- package/dist/types/core/timeline/index.d.ts +1 -0
- package/dist/types/core/timeline/timeline.d.ts +34 -0
- package/dist/types/ecs/component.d.ts +67 -11
- package/dist/types/ecs/entity-handle.d.ts +5 -5
- package/dist/types/ecs/system-builder.d.ts +13 -0
- package/dist/types/ecs/world.d.ts +72 -4
- package/dist/types/game/loop/loop.d.ts +51 -2
- package/dist/types/game/loop/ticker-schedule.d.ts +52 -0
- package/dist/types/net/adapters/bun-websocket.d.ts +19 -3
- package/dist/types/renderer/index.d.ts +1 -0
- package/dist/types/renderer/prefab-bucket/concrete.d.ts +16 -6
- package/dist/types/renderer/prefab-bucket/index.d.ts +11 -7
- package/dist/types/renderer/prefab-bucket/specs.d.ts +10 -0
- package/dist/types/renderer/raycast/index.d.ts +1 -0
- package/dist/types/renderer/raycast/raycast.d.ts +24 -0
- package/dist/types/renderer/types.d.ts +1 -0
- package/dist/webgpu/cjs/index.js +1897 -592
- package/dist/webgpu/esm/index.js +1889 -578
- package/dist/webgpu/types/2d/raycast.d.ts +45 -0
- package/dist/webgpu/types/2d/renderer.d.ts +11 -0
- package/dist/webgpu/types/2d/sprite-accessor.d.ts +3 -1
- package/dist/webgpu/types/3d/hitbox.d.ts +32 -0
- package/dist/webgpu/types/3d/lights.d.ts +113 -0
- package/dist/webgpu/types/3d/lights.test.d.ts +1 -0
- package/dist/webgpu/types/3d/raycast.d.ts +44 -0
- package/dist/webgpu/types/3d/renderer.d.ts +88 -1
- package/dist/webgpu/types/3d/shader.d.ts +88 -5
- package/dist/webgpu/types/core/types.d.ts +55 -0
- package/dist/webgpu/types/geometry/geometry-builder.d.ts +1 -4
- package/dist/webgpu/types/index.d.ts +1 -0
- package/dist/webgpu/types/shaders/utils.d.ts +24 -0
- 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
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
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
|
|
8
|
-
|
|
6
|
+
export declare class PredictionLog<Cmd> {
|
|
7
|
+
private capacity;
|
|
8
|
+
private entries;
|
|
9
|
+
constructor(capacity?: number);
|
|
9
10
|
get size(): number;
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
|
|
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
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
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
|
|
36
|
-
private
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
*
|
|
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,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';
|