@sadhaka/loom-engine 0.11.0 → 0.13.0
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/COMMERCIAL_LICENSE_TERMS.md +74 -0
- package/README.md +230 -6
- package/dist/components/peer-sprite.d.ts +25 -0
- package/dist/components/peer-sprite.d.ts.map +1 -0
- package/dist/components/peer-sprite.js +48 -0
- package/dist/components/peer-sprite.js.map +1 -0
- package/dist/engine.d.ts +6 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +35 -1
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +19 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -2
- package/dist/index.js.map +1 -1
- package/dist/network/mock-multiplayer-bridge.d.ts +34 -0
- package/dist/network/mock-multiplayer-bridge.d.ts.map +1 -0
- package/dist/network/mock-multiplayer-bridge.js +91 -0
- package/dist/network/mock-multiplayer-bridge.js.map +1 -0
- package/dist/network/multiplayer-bridge.d.ts +45 -0
- package/dist/network/multiplayer-bridge.d.ts.map +1 -0
- package/dist/network/multiplayer-bridge.js +34 -0
- package/dist/network/multiplayer-bridge.js.map +1 -0
- package/dist/network/peer-pool.d.ts +46 -0
- package/dist/network/peer-pool.d.ts.map +1 -0
- package/dist/network/peer-pool.js +185 -0
- package/dist/network/peer-pool.js.map +1 -0
- package/dist/network/sse-multiplayer-bridge.d.ts +36 -0
- package/dist/network/sse-multiplayer-bridge.d.ts.map +1 -0
- package/dist/network/sse-multiplayer-bridge.js +264 -0
- package/dist/network/sse-multiplayer-bridge.js.map +1 -0
- package/dist/renderer/shaders/sprite-shader-source.d.ts +4 -0
- package/dist/renderer/shaders/sprite-shader-source.d.ts.map +1 -0
- package/dist/renderer/shaders/sprite-shader-source.js +83 -0
- package/dist/renderer/shaders/sprite-shader-source.js.map +1 -0
- package/dist/renderer/sprite-batcher.d.ts +30 -0
- package/dist/renderer/sprite-batcher.d.ts.map +1 -0
- package/dist/renderer/sprite-batcher.js +146 -0
- package/dist/renderer/sprite-batcher.js.map +1 -0
- package/dist/renderer/texture-atlas.d.ts +24 -0
- package/dist/renderer/texture-atlas.d.ts.map +1 -0
- package/dist/renderer/texture-atlas.js +173 -0
- package/dist/renderer/texture-atlas.js.map +1 -0
- package/dist/renderer/webgl2-device.d.ts +53 -0
- package/dist/renderer/webgl2-device.d.ts.map +1 -0
- package/dist/renderer/webgl2-device.js +596 -0
- package/dist/renderer/webgl2-device.js.map +1 -0
- package/dist/systems/peer-presence-system.d.ts +19 -0
- package/dist/systems/peer-presence-system.d.ts.map +1 -0
- package/dist/systems/peer-presence-system.js +118 -0
- package/dist/systems/peer-presence-system.js.map +1 -0
- package/package.json +11 -5
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// the previous suffix `-perf-9-1` lingered after package.json was
|
|
9
9
|
// bumped to 0.10.0, surfacing as a drift bug in
|
|
10
10
|
// engine.LOOM_ENGINE_VERSION-based diagnostics.
|
|
11
|
-
export const LOOM_ENGINE_VERSION = '0.
|
|
11
|
+
export const LOOM_ENGINE_VERSION = '0.13.0';
|
|
12
12
|
export { vec2, vec3, rect, clamp, lerp, smoothstep, approxEq, rectContains, rectIntersects, visibleInView, } from './util/math.js';
|
|
13
13
|
export { rgba, hexToRgba, rgbaToHexString, rgbaToCssString, colorLerp, COLOR_WHITE, COLOR_BLACK, COLOR_TRANSPARENT, COLOR_KNOT_STR, COLOR_KNOT_DEX, COLOR_KNOT_INT, COLOR_KNOT_CENTER, } from './util/color.js';
|
|
14
14
|
export { EntityAllocator, NULL_ENTITY, entityIndex, entityGeneration, makeEntity, } from './entity.js';
|
|
@@ -21,9 +21,18 @@ export { ResourceRegistry, createTimeResource, createVeilBudgetResource, RESOURC
|
|
|
21
21
|
// Default systems
|
|
22
22
|
export { SpriteRenderSystem } from './systems/sprite-render-system.js';
|
|
23
23
|
// Engine facade
|
|
24
|
-
export { Engine } from './engine.js';
|
|
24
|
+
export { Engine, registerBackend, isBackendRegistered } from './engine.js';
|
|
25
25
|
export { createCamera, getCameraViewRect, worldToScreen, screenToWorld, } from './renderer/camera.js';
|
|
26
26
|
export { Canvas2DDevice, } from './renderer/canvas2d-device.js';
|
|
27
|
+
// WebGL2 backend (Phase 14.1). Importing this symbol triggers the
|
|
28
|
+
// module's self-registration of the 'webgl2' backend factory, so
|
|
29
|
+
// Engine.create({ backend: 'webgl2' }) starts working from then on.
|
|
30
|
+
// Canvas2D-only consumers do not import this and the entire WebGL2
|
|
31
|
+
// path tree-shakes out of the bundle.
|
|
32
|
+
export { WebGL2Device } from './renderer/webgl2-device.js';
|
|
33
|
+
export { TextureAtlas, makeParticleDiscAtlas, } from './renderer/texture-atlas.js';
|
|
34
|
+
export { SpriteBatcher, FLOATS_PER_INSTANCE, } from './renderer/sprite-batcher.js';
|
|
35
|
+
export { SPRITE_VERT_SRC, SPRITE_FRAG_SRC, UNIT_QUAD_VERTICES, } from './renderer/shaders/sprite-shader-source.js';
|
|
27
36
|
export { ISO_TILE_WIDTH, ISO_TILE_HEIGHT, ISO_HALF_W, ISO_HALF_H, ISO_Z_SCALE, tileToIso, worldToIso, isoToTile, isoDepthKey, } from './renderer/iso-projection.js';
|
|
28
37
|
export { loadSpriteSheet, computeFrameIndex, SpriteSheetLoadError, } from './asset/sprite-sheet-loader.js';
|
|
29
38
|
export { synthesizeDefaultClip, clipDurationMs, frameInClipAt, manifestFrameIndex, } from './animation/animation-clip.js';
|
|
@@ -66,4 +75,10 @@ export { createZoneState, beginTransition, tickTransition, isTransitioning, RESO
|
|
|
66
75
|
export { ZONE_CATALOG } from './zone/zone-catalog.js';
|
|
67
76
|
export { InteractablePool, POOL_INTERACTABLE, INTERACTABLE_FLAG_ACTIVE, } from './components/interactable.js';
|
|
68
77
|
export { InteractionSystem, createLastInteraction, RESOURCE_LAST_INTERACTION, } from './systems/interaction-system.js';
|
|
78
|
+
export { RESOURCE_MULTIPLAYER_BRIDGE, RESOURCE_PEER_POOL, BROADCAST_HZ, BROADCAST_MIN_INTERVAL_MS, } from './network/multiplayer-bridge.js';
|
|
79
|
+
export { PeerPool } from './network/peer-pool.js';
|
|
80
|
+
export { MockMultiplayerBridge } from './network/mock-multiplayer-bridge.js';
|
|
81
|
+
export { SSEMultiplayerBridge } from './network/sse-multiplayer-bridge.js';
|
|
82
|
+
export { PeerSpritePool, POOL_PEER_SPRITE } from './components/peer-sprite.js';
|
|
83
|
+
export { PeerPresenceSystem, PeerRenderSystem, } from './systems/peer-presence-system.js';
|
|
69
84
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,EAAE;AACF,kEAAkE;AAClE,mEAAmE;AACnE,qDAAqD;AAErD,qEAAqE;AACrE,kEAAkE;AAClE,kEAAkE;AAClE,gDAAgD;AAChD,gDAAgD;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,QAAQ,CAAC;AAI5C,OAAO,EACL,IAAI,EACJ,IAAI,EACJ,IAAI,EACJ,KAAK,EACL,IAAI,EACJ,UAAU,EACV,QAAQ,EACR,YAAY,EACZ,cAAc,EACd,aAAa,GACd,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,IAAI,EACJ,SAAS,EACT,eAAe,EACf,eAAe,EACf,SAAS,EACT,WAAW,EACX,WAAW,EACX,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,cAAc,EACd,iBAAiB,GAClB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EACL,eAAe,EACf,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,aAAa,EACb,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACL,UAAU,EACV,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAEhC,WAAW;AACX,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEhE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,EACpB,sBAAsB,EACtB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,wBAAwB,EACxB,aAAa,EACb,eAAe,EACf,eAAe,EACf,oBAAoB,GACrB,MAAM,gBAAgB,CAAC;AAExB,kBAAkB;AAClB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AAEvE,gBAAgB;AAChB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type IMultiplayerBridge, type MultiplayerBridgeStatus, type MultiplayerBridgeStats, type PresenceMessage } from './multiplayer-bridge.js';
|
|
2
|
+
export interface MockMultiplayerBridgeOptions {
|
|
3
|
+
nowMs?: () => number;
|
|
4
|
+
}
|
|
5
|
+
interface SentBroadcast {
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
zone: string;
|
|
9
|
+
tsMs: number;
|
|
10
|
+
sentAtMs: number;
|
|
11
|
+
}
|
|
12
|
+
export declare class MockMultiplayerBridge implements IMultiplayerBridge {
|
|
13
|
+
private queue;
|
|
14
|
+
private statusValue;
|
|
15
|
+
private statsValue;
|
|
16
|
+
private readonly nowMs;
|
|
17
|
+
private lastBroadcastMs;
|
|
18
|
+
private sentBroadcasts;
|
|
19
|
+
constructor(opts?: MockMultiplayerBridgeOptions);
|
|
20
|
+
connect(): void;
|
|
21
|
+
disconnect(): void;
|
|
22
|
+
status(): MultiplayerBridgeStatus;
|
|
23
|
+
pollMessages(): PresenceMessage[];
|
|
24
|
+
broadcastPosition(x: number, y: number, zone: string, tsMs: number): void;
|
|
25
|
+
stats(): Readonly<MultiplayerBridgeStats>;
|
|
26
|
+
enqueueIncoming(msg: PresenceMessage): void;
|
|
27
|
+
enqueueIncomingAll(msgs: ReadonlyArray<PresenceMessage>): void;
|
|
28
|
+
getSentBroadcasts(): ReadonlyArray<Readonly<SentBroadcast>>;
|
|
29
|
+
pendingIncoming(): number;
|
|
30
|
+
resetRateLimit(): void;
|
|
31
|
+
getLastBroadcastMs(): number;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
34
|
+
//# sourceMappingURL=mock-multiplayer-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-multiplayer-bridge.d.ts","sourceRoot":"","sources":["../../src/network/mock-multiplayer-bridge.ts"],"names":[],"mappings":"AAYA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,4BAA4B;IAI3C,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AAED,UAAU,aAAa;IACrB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IAIb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,qBAAsB,YAAW,kBAAkB;IAC9D,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,UAAU,CAKhB;IACF,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,cAAc,CAAuB;gBAEjC,IAAI,GAAE,4BAAiC;IAInD,OAAO,IAAI,IAAI;IAKf,UAAU,IAAI,IAAI;IAIlB,MAAM,IAAI,uBAAuB;IAIjC,YAAY,IAAI,eAAe,EAAE;IAOjC,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAWzE,KAAK,IAAI,QAAQ,CAAC,sBAAsB,CAAC;IAOzC,eAAe,CAAC,GAAG,EAAE,eAAe,GAAG,IAAI;IAK3C,kBAAkB,CAAC,IAAI,EAAE,aAAa,CAAC,eAAe,CAAC,GAAG,IAAI;IAS9D,iBAAiB,IAAI,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC;IAK3D,eAAe,IAAI,MAAM;IAMzB,cAAc,IAAI,IAAI;IAKtB,kBAAkB,IAAI,MAAM;CAG7B"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
// MockMultiplayerBridge - in-memory presence transport for tests +
|
|
2
|
+
// offline demos.
|
|
3
|
+
//
|
|
4
|
+
// Tests inject incoming presence frames via enqueueIncoming() and let
|
|
5
|
+
// PeerPresenceSystem drain them. broadcastPosition() captures local
|
|
6
|
+
// sends in a getSentBroadcasts() buffer so tests can assert on them.
|
|
7
|
+
// pollMessages() drains the inbound queue in FIFO order.
|
|
8
|
+
//
|
|
9
|
+
// Rate-limiting: shared with the SSE bridge via BROADCAST_MIN_INTERVAL_MS.
|
|
10
|
+
// A nowMs() injection point lets tests fast-forward time without
|
|
11
|
+
// touching the real clock.
|
|
12
|
+
import { BROADCAST_MIN_INTERVAL_MS, } from './multiplayer-bridge.js';
|
|
13
|
+
export class MockMultiplayerBridge {
|
|
14
|
+
queue = [];
|
|
15
|
+
statusValue = 'idle';
|
|
16
|
+
statsValue = {
|
|
17
|
+
messagesReceived: 0,
|
|
18
|
+
messagesSent: 0,
|
|
19
|
+
rateLimitedDrops: 0,
|
|
20
|
+
reconnects: 0,
|
|
21
|
+
};
|
|
22
|
+
nowMs;
|
|
23
|
+
lastBroadcastMs = -Infinity;
|
|
24
|
+
sentBroadcasts = [];
|
|
25
|
+
constructor(opts = {}) {
|
|
26
|
+
this.nowMs = opts.nowMs ?? (() => (typeof performance !== 'undefined' ? performance.now() : Date.now()));
|
|
27
|
+
}
|
|
28
|
+
connect() {
|
|
29
|
+
if (this.statusValue === 'connected')
|
|
30
|
+
return;
|
|
31
|
+
this.statusValue = 'connected';
|
|
32
|
+
}
|
|
33
|
+
disconnect() {
|
|
34
|
+
this.statusValue = 'closed';
|
|
35
|
+
}
|
|
36
|
+
status() {
|
|
37
|
+
return this.statusValue;
|
|
38
|
+
}
|
|
39
|
+
pollMessages() {
|
|
40
|
+
if (this.queue.length === 0)
|
|
41
|
+
return [];
|
|
42
|
+
const out = this.queue;
|
|
43
|
+
this.queue = [];
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
46
|
+
broadcastPosition(x, y, zone, tsMs) {
|
|
47
|
+
const now = this.nowMs();
|
|
48
|
+
if (now - this.lastBroadcastMs < BROADCAST_MIN_INTERVAL_MS) {
|
|
49
|
+
this.statsValue.rateLimitedDrops++;
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
this.lastBroadcastMs = now;
|
|
53
|
+
this.sentBroadcasts.push({ x, y, zone, tsMs, sentAtMs: now });
|
|
54
|
+
this.statsValue.messagesSent++;
|
|
55
|
+
}
|
|
56
|
+
stats() {
|
|
57
|
+
return this.statsValue;
|
|
58
|
+
}
|
|
59
|
+
// ----- Mock-only injection helpers -----
|
|
60
|
+
// Enqueue an inbound presence message as if it came from the server.
|
|
61
|
+
enqueueIncoming(msg) {
|
|
62
|
+
this.queue.push(msg);
|
|
63
|
+
this.statsValue.messagesReceived++;
|
|
64
|
+
}
|
|
65
|
+
enqueueIncomingAll(msgs) {
|
|
66
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
67
|
+
const m = msgs[i];
|
|
68
|
+
if (m)
|
|
69
|
+
this.enqueueIncoming(m);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Tests inspect what broadcastPosition let through. Returns the
|
|
73
|
+
// backing array; the caller may copy if they need a snapshot.
|
|
74
|
+
getSentBroadcasts() {
|
|
75
|
+
return this.sentBroadcasts;
|
|
76
|
+
}
|
|
77
|
+
// Convenience: how many inbound messages are waiting for a poll.
|
|
78
|
+
pendingIncoming() {
|
|
79
|
+
return this.queue.length;
|
|
80
|
+
}
|
|
81
|
+
// Reset rate-limiter state. Useful in tests that simulate a
|
|
82
|
+
// reconnect or want a clean slate.
|
|
83
|
+
resetRateLimit() {
|
|
84
|
+
this.lastBroadcastMs = -Infinity;
|
|
85
|
+
}
|
|
86
|
+
// Inspect-only: when did broadcastPosition last send (in nowMs() units).
|
|
87
|
+
getLastBroadcastMs() {
|
|
88
|
+
return this.lastBroadcastMs;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=mock-multiplayer-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock-multiplayer-bridge.js","sourceRoot":"","sources":["../../src/network/mock-multiplayer-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export type MultiplayerBridgeStatus = 'idle' | 'connecting' | 'connected' | 'reconnecting' | 'closed';
|
|
2
|
+
export interface MultiplayerBridgeStats {
|
|
3
|
+
messagesReceived: number;
|
|
4
|
+
messagesSent: number;
|
|
5
|
+
rateLimitedDrops: number;
|
|
6
|
+
reconnects: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PresenceUpdate {
|
|
9
|
+
kind: 'update';
|
|
10
|
+
characterId: string;
|
|
11
|
+
x: number;
|
|
12
|
+
y: number;
|
|
13
|
+
zone: string;
|
|
14
|
+
tsMs: number;
|
|
15
|
+
name?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface PresenceDepart {
|
|
18
|
+
kind: 'depart';
|
|
19
|
+
characterId: string;
|
|
20
|
+
}
|
|
21
|
+
export interface PresenceSnapshot {
|
|
22
|
+
kind: 'snapshot';
|
|
23
|
+
peers: ReadonlyArray<{
|
|
24
|
+
characterId: string;
|
|
25
|
+
x: number;
|
|
26
|
+
y: number;
|
|
27
|
+
zone: string;
|
|
28
|
+
tsMs: number;
|
|
29
|
+
name?: string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export type PresenceMessage = PresenceUpdate | PresenceDepart | PresenceSnapshot;
|
|
33
|
+
export interface IMultiplayerBridge {
|
|
34
|
+
connect(): void;
|
|
35
|
+
disconnect(): void;
|
|
36
|
+
status(): MultiplayerBridgeStatus;
|
|
37
|
+
pollMessages(): PresenceMessage[];
|
|
38
|
+
broadcastPosition(x: number, y: number, zone: string, tsMs: number): void;
|
|
39
|
+
stats(): Readonly<MultiplayerBridgeStats>;
|
|
40
|
+
}
|
|
41
|
+
export declare const RESOURCE_MULTIPLAYER_BRIDGE = "multiplayer_bridge";
|
|
42
|
+
export declare const RESOURCE_PEER_POOL = "peer_pool";
|
|
43
|
+
export declare const BROADCAST_HZ = 10;
|
|
44
|
+
export declare const BROADCAST_MIN_INTERVAL_MS: number;
|
|
45
|
+
//# sourceMappingURL=multiplayer-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiplayer-bridge.d.ts","sourceRoot":"","sources":["../../src/network/multiplayer-bridge.ts"],"names":[],"mappings":"AAyBA,MAAM,MAAM,uBAAuB,GAC/B,MAAM,GACN,YAAY,GACZ,WAAW,GACX,cAAc,GACd,QAAQ,CAAC;AAEb,MAAM,WAAW,sBAAsB;IAErC,gBAAgB,EAAE,MAAM,CAAC;IAGzB,YAAY,EAAE,MAAM,CAAC;IAGrB,gBAAgB,EAAE,MAAM,CAAC;IAEzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAKD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAGD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,aAAa,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,CAAC;CACJ;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,cAAc,GAAG,gBAAgB,CAAC;AAEjF,MAAM,WAAW,kBAAkB;IAGjC,OAAO,IAAI,IAAI,CAAC;IAEhB,UAAU,IAAI,IAAI,CAAC;IACnB,MAAM,IAAI,uBAAuB,CAAC;IAGlC,YAAY,IAAI,eAAe,EAAE,CAAC;IAKlC,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1E,KAAK,IAAI,QAAQ,CAAC,sBAAsB,CAAC,CAAC;CAC3C;AAGD,eAAO,MAAM,2BAA2B,uBAAuB,CAAC;AAChE,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAM9C,eAAO,MAAM,YAAY,KAAK,CAAC;AAC/B,eAAO,MAAM,yBAAyB,QAAsB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// MultiplayerBridge - abstraction over the presence transport.
|
|
2
|
+
//
|
|
3
|
+
// Concrete implementations:
|
|
4
|
+
// MockMultiplayerBridge - in-process transport for tests + offline
|
|
5
|
+
// dev. enqueueIncoming() simulates server
|
|
6
|
+
// pushes; broadcastPosition() captures local
|
|
7
|
+
// sends so tests can assert on them.
|
|
8
|
+
// SSEMultiplayerBridge - SSE-based implementation parallel to
|
|
9
|
+
// SSEDirectorBridge. Browser-only; throws in
|
|
10
|
+
// Node test runs.
|
|
11
|
+
//
|
|
12
|
+
// The PeerPresenceSystem (PHASE_INPUT) calls pollMessages() once per
|
|
13
|
+
// tick to drain queued presence updates, applies them to the PeerPool,
|
|
14
|
+
// and advances per-peer interpolation. Bridges buffer messages between
|
|
15
|
+
// polls so the system's per-tick read is bounded.
|
|
16
|
+
//
|
|
17
|
+
// Wire protocol (shared with the server-side Track B implementation):
|
|
18
|
+
// - Server emits SSE 'presence.update' { character_id, x, y, zone, ts_ms, name? }
|
|
19
|
+
// - Server emits SSE 'presence.depart' { character_id }
|
|
20
|
+
// - Server emits SSE 'presence.snapshot' { peers: [{ character_id, x, y, zone, ts_ms, name? }, ...] }
|
|
21
|
+
// - Client POSTs /presence/move { character_id, x, y, zone, ts_ms }
|
|
22
|
+
//
|
|
23
|
+
// The bridge layer hides transport details. PeerPresenceSystem and
|
|
24
|
+
// PeerPool deal only with strongly-typed PresenceMessage values.
|
|
25
|
+
// Resource keys.
|
|
26
|
+
export const RESOURCE_MULTIPLAYER_BRIDGE = 'multiplayer_bridge';
|
|
27
|
+
export const RESOURCE_PEER_POOL = 'peer_pool';
|
|
28
|
+
// Engine-side rate limit on broadcastPosition. The wire protocol
|
|
29
|
+
// targets 10 Hz; bridges enforce this regardless of how often the
|
|
30
|
+
// caller invokes broadcastPosition. Documented in the Multiplayer
|
|
31
|
+
// section of README.md.
|
|
32
|
+
export const BROADCAST_HZ = 10;
|
|
33
|
+
export const BROADCAST_MIN_INTERVAL_MS = 1000 / BROADCAST_HZ;
|
|
34
|
+
//# sourceMappingURL=multiplayer-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"multiplayer-bridge.js","sourceRoot":"","sources":["../../src/network/multiplayer-bridge.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,EAAE;AACF,4BAA4B;AAC5B,qEAAqE;AACrE,oEAAoE;AACpE,uEAAuE;AACvE,+DAA+D;AAC/D,iEAAiE;AACjE,uEAAuE;AACvE,4CAA4C;AAC5C,EAAE;AACF,qEAAqE;AACrE,uEAAuE;AACvE,uEAAuE;AACvE,kDAAkD;AAClD,EAAE;AACF,sEAAsE;AACtE,sFAAsF;AACtF,4DAA4D;AAC5D,wGAAwG;AACxG,+EAA+E;AAC/E,EAAE;AACF,mEAAmE;AACnE,iEAAiE;AA2EjE,iBAAiB;AACjB,MAAM,CAAC,MAAM,2BAA2B,GAAG,oBAAoB,CAAC;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAW,CAAC;AAE9C,iEAAiE;AACjE,kEAAkE;AAClE,kEAAkE;AAClE,wBAAwB;AACxB,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,CAAC;AAC/B,MAAM,CAAC,MAAM,yBAAyB,GAAG,IAAI,GAAG,YAAY,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
export interface PeerEntry {
|
|
2
|
+
characterId: string;
|
|
3
|
+
zone: string;
|
|
4
|
+
name: string | null;
|
|
5
|
+
prevX: number;
|
|
6
|
+
prevY: number;
|
|
7
|
+
prevTsMs: number;
|
|
8
|
+
currentX: number;
|
|
9
|
+
currentY: number;
|
|
10
|
+
currentTsMs: number;
|
|
11
|
+
lastRenderedFrame: number;
|
|
12
|
+
}
|
|
13
|
+
export interface RenderedPeerView {
|
|
14
|
+
characterId: string;
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
zone: string;
|
|
18
|
+
name: string | null;
|
|
19
|
+
}
|
|
20
|
+
export declare class PeerPool {
|
|
21
|
+
private peers;
|
|
22
|
+
private localCharacterId;
|
|
23
|
+
private scratchView;
|
|
24
|
+
setLocalCharacterId(id: string | null): void;
|
|
25
|
+
getLocalCharacterId(): string | null;
|
|
26
|
+
upsert(characterId: string, x: number, y: number, zone: string, tsMs: number, name?: string): void;
|
|
27
|
+
applySnapshot(peers: ReadonlyArray<{
|
|
28
|
+
characterId: string;
|
|
29
|
+
x: number;
|
|
30
|
+
y: number;
|
|
31
|
+
zone: string;
|
|
32
|
+
tsMs: number;
|
|
33
|
+
name?: string;
|
|
34
|
+
}>): void;
|
|
35
|
+
remove(characterId: string): boolean;
|
|
36
|
+
has(characterId: string): boolean;
|
|
37
|
+
size(): number;
|
|
38
|
+
get(characterId: string): Readonly<PeerEntry> | undefined;
|
|
39
|
+
forEachRendered(nowMs: number, frame: number, fn: (view: Readonly<RenderedPeerView>) => void): void;
|
|
40
|
+
getRenderedPosition(characterId: string, nowMs: number): {
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
} | null;
|
|
44
|
+
clear(): void;
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=peer-pool.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer-pool.d.ts","sourceRoot":"","sources":["../../src/network/peer-pool.ts"],"names":[],"mappings":"AA2BA,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAGpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IAGpB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAMD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAYD,qBAAa,QAAQ;IACnB,OAAO,CAAC,KAAK,CAAqC;IAClD,OAAO,CAAC,gBAAgB,CAAuB;IAI/C,OAAO,CAAC,WAAW,CAMjB;IAEF,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAO5C,mBAAmB,IAAI,MAAM,GAAG,IAAI;IASpC,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IA0ClG,aAAa,CACX,KAAK,EAAE,aAAa,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,CAAC,EAAE,MAAM,CAAC;QACV,CAAC,EAAE,MAAM,CAAC;QACV,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC,GACD,IAAI;IAwBP,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIpC,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIjC,IAAI,IAAI,MAAM;IAId,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,SAAS;IAOzD,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,gBAAgB,CAAC,KAAK,IAAI,GAAG,IAAI;IAsBnG,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAcxF,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
// PeerPool - tracks all known remote peers and their interpolated
|
|
2
|
+
// world position.
|
|
3
|
+
//
|
|
4
|
+
// Each peer keeps the last two known positions (prev, current) with
|
|
5
|
+
// timestamps. At frame time the system asks the pool for the
|
|
6
|
+
// interpolated (x, y) per peer, which is computed as
|
|
7
|
+
// factor = clamp01((nowMs - prevTsMs) / (currentTsMs - prevTsMs))
|
|
8
|
+
// x = lerp(prevX, currentX, factor)
|
|
9
|
+
// y = lerp(prevY, currentY, factor)
|
|
10
|
+
//
|
|
11
|
+
// When a new presence.update arrives, prev <- current, current <- new.
|
|
12
|
+
// The factor saturates at 1 once nowMs passes currentTsMs, so a peer
|
|
13
|
+
// who stops sending updates simply freezes at their last known
|
|
14
|
+
// position rather than extrapolating off into the distance.
|
|
15
|
+
//
|
|
16
|
+
// "Acceptable lag" per the phase 15.1 spec is ~150ms (one update
|
|
17
|
+
// interval at the 10Hz wire rate), which is imperceptible at
|
|
18
|
+
// walk-speed. No CRDT, no client-side prediction beyond the
|
|
19
|
+
// straight-line lerp - those are deferred until shared state extends
|
|
20
|
+
// past raw position.
|
|
21
|
+
//
|
|
22
|
+
// Self-filter: the local character's own character_id should NOT
|
|
23
|
+
// appear among the rendered peers (we don't render ourselves as a
|
|
24
|
+
// ghost). The PeerPresenceSystem owns this filter via
|
|
25
|
+
// setLocalCharacterId(); peers with that id are silently skipped on
|
|
26
|
+
// upsert and removed if already present.
|
|
27
|
+
function lerp(a, b, t) {
|
|
28
|
+
return a + (b - a) * t;
|
|
29
|
+
}
|
|
30
|
+
function clamp01(t) {
|
|
31
|
+
if (t < 0)
|
|
32
|
+
return 0;
|
|
33
|
+
if (t > 1)
|
|
34
|
+
return 1;
|
|
35
|
+
return t;
|
|
36
|
+
}
|
|
37
|
+
export class PeerPool {
|
|
38
|
+
peers = new Map();
|
|
39
|
+
localCharacterId = null;
|
|
40
|
+
// Reused on every getRenderedPosition / forEachRendered call so the
|
|
41
|
+
// hot per-frame path is allocation-free.
|
|
42
|
+
scratchView = {
|
|
43
|
+
characterId: '',
|
|
44
|
+
x: 0,
|
|
45
|
+
y: 0,
|
|
46
|
+
zone: '',
|
|
47
|
+
name: null,
|
|
48
|
+
};
|
|
49
|
+
setLocalCharacterId(id) {
|
|
50
|
+
this.localCharacterId = id;
|
|
51
|
+
if (id !== null && this.peers.has(id)) {
|
|
52
|
+
this.peers.delete(id);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
getLocalCharacterId() {
|
|
56
|
+
return this.localCharacterId;
|
|
57
|
+
}
|
|
58
|
+
// Apply a new presence update for a peer. If the peer is the local
|
|
59
|
+
// character, the update is ignored (self-filter). If this is the
|
|
60
|
+
// first update for the peer, prev = current = the new position so
|
|
61
|
+
// the lerp factor immediately saturates and the peer renders at the
|
|
62
|
+
// sent position.
|
|
63
|
+
upsert(characterId, x, y, zone, tsMs, name) {
|
|
64
|
+
if (this.localCharacterId !== null && characterId === this.localCharacterId) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const existing = this.peers.get(characterId);
|
|
68
|
+
if (!existing) {
|
|
69
|
+
this.peers.set(characterId, {
|
|
70
|
+
characterId,
|
|
71
|
+
zone,
|
|
72
|
+
name: name ?? null,
|
|
73
|
+
prevX: x,
|
|
74
|
+
prevY: y,
|
|
75
|
+
prevTsMs: tsMs,
|
|
76
|
+
currentX: x,
|
|
77
|
+
currentY: y,
|
|
78
|
+
currentTsMs: tsMs,
|
|
79
|
+
lastRenderedFrame: -1,
|
|
80
|
+
});
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Out-of-order: drop messages older than current. Wire protocol
|
|
84
|
+
// is monotonic per character_id, but reorder buffers + reconnect
|
|
85
|
+
// replays can deliver an older ts after a newer one.
|
|
86
|
+
if (tsMs < existing.currentTsMs) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
existing.prevX = existing.currentX;
|
|
90
|
+
existing.prevY = existing.currentY;
|
|
91
|
+
existing.prevTsMs = existing.currentTsMs;
|
|
92
|
+
existing.currentX = x;
|
|
93
|
+
existing.currentY = y;
|
|
94
|
+
existing.currentTsMs = tsMs;
|
|
95
|
+
existing.zone = zone;
|
|
96
|
+
if (name !== undefined) {
|
|
97
|
+
existing.name = name;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Replace the entire roster with a snapshot. Peers not present in
|
|
101
|
+
// the snapshot are dropped; peers in the snapshot but not yet
|
|
102
|
+
// tracked are inserted (with prev = current so they render at the
|
|
103
|
+
// sent position immediately).
|
|
104
|
+
applySnapshot(peers) {
|
|
105
|
+
const seen = new Set();
|
|
106
|
+
for (let i = 0; i < peers.length; i++) {
|
|
107
|
+
const p = peers[i];
|
|
108
|
+
if (!p)
|
|
109
|
+
continue;
|
|
110
|
+
if (this.localCharacterId !== null && p.characterId === this.localCharacterId) {
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
seen.add(p.characterId);
|
|
114
|
+
this.upsert(p.characterId, p.x, p.y, p.zone, p.tsMs, p.name);
|
|
115
|
+
}
|
|
116
|
+
// Drop anyone not in the snapshot. Iterate keys snapshot first
|
|
117
|
+
// because Map.delete during iteration is fine but copying makes
|
|
118
|
+
// intent obvious.
|
|
119
|
+
const toRemove = [];
|
|
120
|
+
this.peers.forEach((_v, k) => {
|
|
121
|
+
if (!seen.has(k))
|
|
122
|
+
toRemove.push(k);
|
|
123
|
+
});
|
|
124
|
+
for (let i = 0; i < toRemove.length; i++) {
|
|
125
|
+
const k = toRemove[i];
|
|
126
|
+
if (k)
|
|
127
|
+
this.peers.delete(k);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
remove(characterId) {
|
|
131
|
+
return this.peers.delete(characterId);
|
|
132
|
+
}
|
|
133
|
+
has(characterId) {
|
|
134
|
+
return this.peers.has(characterId);
|
|
135
|
+
}
|
|
136
|
+
size() {
|
|
137
|
+
return this.peers.size;
|
|
138
|
+
}
|
|
139
|
+
get(characterId) {
|
|
140
|
+
return this.peers.get(characterId);
|
|
141
|
+
}
|
|
142
|
+
// Iterate every tracked peer with their interpolated world
|
|
143
|
+
// position at nowMs. The view object is reused; consumers must
|
|
144
|
+
// copy any field they want to retain past the callback.
|
|
145
|
+
forEachRendered(nowMs, frame, fn) {
|
|
146
|
+
this.peers.forEach((entry) => {
|
|
147
|
+
const v = this.scratchView;
|
|
148
|
+
v.characterId = entry.characterId;
|
|
149
|
+
v.zone = entry.zone;
|
|
150
|
+
v.name = entry.name;
|
|
151
|
+
const dt = entry.currentTsMs - entry.prevTsMs;
|
|
152
|
+
if (dt <= 0) {
|
|
153
|
+
v.x = entry.currentX;
|
|
154
|
+
v.y = entry.currentY;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
const t = clamp01((nowMs - entry.prevTsMs) / dt);
|
|
158
|
+
v.x = lerp(entry.prevX, entry.currentX, t);
|
|
159
|
+
v.y = lerp(entry.prevY, entry.currentY, t);
|
|
160
|
+
}
|
|
161
|
+
entry.lastRenderedFrame = frame;
|
|
162
|
+
fn(v);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// Single-peer query for tests + the rare ad-hoc lookup. Hot paths
|
|
166
|
+
// use forEachRendered to avoid map lookups.
|
|
167
|
+
getRenderedPosition(characterId, nowMs) {
|
|
168
|
+
const entry = this.peers.get(characterId);
|
|
169
|
+
if (!entry)
|
|
170
|
+
return null;
|
|
171
|
+
const dt = entry.currentTsMs - entry.prevTsMs;
|
|
172
|
+
if (dt <= 0) {
|
|
173
|
+
return { x: entry.currentX, y: entry.currentY };
|
|
174
|
+
}
|
|
175
|
+
const t = clamp01((nowMs - entry.prevTsMs) / dt);
|
|
176
|
+
return {
|
|
177
|
+
x: lerp(entry.prevX, entry.currentX, t),
|
|
178
|
+
y: lerp(entry.prevY, entry.currentY, t),
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
clear() {
|
|
182
|
+
this.peers.clear();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
//# sourceMappingURL=peer-pool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"peer-pool.js","sourceRoot":"","sources":["../../src/network/peer-pool.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,kBAAkB;AAClB,EAAE;AACF,oEAAoE;AACpE,6DAA6D;AAC7D,qDAAqD;AACrD,oEAAoE;AACpE,2CAA2C;AAC3C,2CAA2C;AAC3C,EAAE;AACF,uEAAuE;AACvE,qEAAqE;AACrE,+DAA+D;AAC/D,4DAA4D;AAC5D,EAAE;AACF,iEAAiE;AACjE,6DAA6D;AAC7D,4DAA4D;AAC5D,qEAAqE;AACrE,qBAAqB;AACrB,EAAE;AACF,iEAAiE;AACjE,kEAAkE;AAClE,sDAAsD;AACtD,oEAAoE;AACpE,yCAAyC;AA+BzC,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS,EAAE,CAAS;IAC3C,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,OAAO,CAAC,CAAS;IACxB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,OAAO,QAAQ;IACX,KAAK,GAA2B,IAAI,GAAG,EAAE,CAAC;IAC1C,gBAAgB,GAAkB,IAAI,CAAC;IAE/C,oEAAoE;IACpE,yCAAyC;IACjC,WAAW,GAAqB;QACtC,WAAW,EAAE,EAAE;QACf,CAAC,EAAE,CAAC;QACJ,CAAC,EAAE,CAAC;QACJ,IAAI,EAAE,EAAE;QACR,IAAI,EAAE,IAAI;KACX,CAAC;IAEF,mBAAmB,CAAC,EAAiB;QACnC,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,IAAI,EAAE,KAAK,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;YACtC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,mBAAmB;QACjB,OAAO,IAAI,CAAC,gBAAgB,CAAC;IAC/B,CAAC;IAED,mEAAmE;IACnE,iEAAiE;IACjE,kEAAkE;IAClE,oEAAoE;IACpE,iBAAiB;IACjB,MAAM,CAAC,WAAmB,EAAE,CAAS,EAAE,CAAS,EAAE,IAAY,EAAE,IAAY,EAAE,IAAa;QACzF,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,IAAI,WAAW,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC5E,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC7C,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,EAAE;gBAC1B,WAAW;gBACX,IAAI;gBACJ,IAAI,EAAE,IAAI,IAAI,IAAI;gBAClB,KAAK,EAAE,CAAC;gBACR,KAAK,EAAE,CAAC;gBACR,QAAQ,EAAE,IAAI;gBACd,QAAQ,EAAE,CAAC;gBACX,QAAQ,EAAE,CAAC;gBACX,WAAW,EAAE,IAAI;gBACjB,iBAAiB,EAAE,CAAC,CAAC;aACtB,CAAC,CAAC;YACH,OAAO;QACT,CAAC;QACD,gEAAgE;QAChE,iEAAiE;QACjE,qDAAqD;QACrD,IAAI,IAAI,GAAG,QAAQ,CAAC,WAAW,EAAE,CAAC;YAChC,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACnC,QAAQ,CAAC,KAAK,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACnC,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC;QACzC,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC;QACtB,QAAQ,CAAC,QAAQ,GAAG,CAAC,CAAC;QACtB,QAAQ,CAAC,WAAW,GAAG,IAAI,CAAC;QAC5B,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACrB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,QAAQ,CAAC,IAAI,GAAG,IAAI,CAAC;QACvB,CAAC;IACH,CAAC;IAED,kEAAkE;IAClE,8DAA8D;IAC9D,kEAAkE;IAClE,8BAA8B;IAC9B,aAAa,CACX,KAOE;QAEF,MAAM,IAAI,GAAgB,IAAI,GAAG,EAAE,CAAC;QACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YACnB,IAAI,CAAC,CAAC;gBAAE,SAAS;YACjB,IAAI,IAAI,CAAC,gBAAgB,KAAK,IAAI,IAAI,CAAC,CAAC,WAAW,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC9E,SAAS;YACX,CAAC;YACD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC;QACD,+DAA+D;QAC/D,gEAAgE;QAChE,kBAAkB;QAClB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE;YAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QACH,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACzC,MAAM,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACtB,IAAI,CAAC;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,MAAM,CAAC,WAAmB;QACxB,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;IACxC,CAAC;IAED,GAAG,CAAC,WAAmB;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,IAAI;QACF,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;IAED,GAAG,CAAC,WAAmB;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACrC,CAAC;IAED,2DAA2D;IAC3D,+DAA+D;IAC/D,wDAAwD;IACxD,eAAe,CAAC,KAAa,EAAE,KAAa,EAAE,EAA8C;QAC1F,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAC3B,MAAM,CAAC,GAAG,IAAI,CAAC,WAAW,CAAC;YAC3B,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YAClC,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YACpB,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YACpB,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC;YAC9C,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;gBACZ,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC;gBACrB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;gBACjD,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;gBAC3C,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAC7C,CAAC;YACD,KAAK,CAAC,iBAAiB,GAAG,KAAK,CAAC;YAChC,EAAE,CAAC,CAAC,CAAC,CAAC;QACR,CAAC,CAAC,CAAC;IACL,CAAC;IAED,kEAAkE;IAClE,4CAA4C;IAC5C,mBAAmB,CAAC,WAAmB,EAAE,KAAa;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,MAAM,EAAE,GAAG,KAAK,CAAC,WAAW,GAAG,KAAK,CAAC,QAAQ,CAAC;QAC9C,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YACZ,OAAO,EAAE,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;QAClD,CAAC;QACD,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC;QACjD,OAAO;YACL,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;YACvC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;SACxC,CAAC;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;CACF"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type IMultiplayerBridge, type MultiplayerBridgeStatus, type MultiplayerBridgeStats, type PresenceMessage } from './multiplayer-bridge.js';
|
|
2
|
+
export interface SSEMultiplayerBridgeOptions {
|
|
3
|
+
baseUrl: string;
|
|
4
|
+
broadcastUrl?: string;
|
|
5
|
+
characterId: string;
|
|
6
|
+
zone: string;
|
|
7
|
+
eventSourceFactory?: (url: string) => EventSource;
|
|
8
|
+
fetchFn?: typeof fetch;
|
|
9
|
+
}
|
|
10
|
+
export declare class SSEMultiplayerBridge implements IMultiplayerBridge {
|
|
11
|
+
private readonly baseUrl;
|
|
12
|
+
private readonly broadcastUrl;
|
|
13
|
+
private readonly characterId;
|
|
14
|
+
private readonly zone;
|
|
15
|
+
private readonly eventSourceFactory;
|
|
16
|
+
private readonly fetchFn;
|
|
17
|
+
private es;
|
|
18
|
+
private queue;
|
|
19
|
+
private statusValue;
|
|
20
|
+
private statsValue;
|
|
21
|
+
private lastBroadcastMs;
|
|
22
|
+
constructor(opts: SSEMultiplayerBridgeOptions);
|
|
23
|
+
connect(): void;
|
|
24
|
+
disconnect(): void;
|
|
25
|
+
status(): MultiplayerBridgeStatus;
|
|
26
|
+
pollMessages(): PresenceMessage[];
|
|
27
|
+
broadcastPosition(x: number, y: number, zone: string, tsMs: number): void;
|
|
28
|
+
stats(): Readonly<MultiplayerBridgeStats>;
|
|
29
|
+
private buildUrl;
|
|
30
|
+
private openConnection;
|
|
31
|
+
private closeConnection;
|
|
32
|
+
private handleUpdate;
|
|
33
|
+
private handleDepart;
|
|
34
|
+
private handleSnapshot;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=sse-multiplayer-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse-multiplayer-bridge.d.ts","sourceRoot":"","sources":["../../src/network/sse-multiplayer-bridge.ts"],"names":[],"mappings":"AA4BA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,eAAe,EAErB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,2BAA2B;IAI1C,OAAO,EAAE,MAAM,CAAC;IAKhB,YAAY,CAAC,EAAE,MAAM,CAAC;IAItB,WAAW,EAAE,MAAM,CAAC;IAEpB,IAAI,EAAE,MAAM,CAAC;IAGb,kBAAkB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,WAAW,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,KAAK,CAAC;CACxB;AAED,qBAAa,oBAAqB,YAAW,kBAAkB;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAA+B;IAClE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAe;IAEvC,OAAO,CAAC,EAAE,CAA4B;IACtC,OAAO,CAAC,KAAK,CAAyB;IACtC,OAAO,CAAC,WAAW,CAAmC;IACtD,OAAO,CAAC,UAAU,CAKhB;IAEF,OAAO,CAAC,eAAe,CAAqB;gBAEhC,IAAI,EAAE,2BAA2B;IAwB7C,OAAO,IAAI,IAAI;IAMf,UAAU,IAAI,IAAI;IAKlB,MAAM,IAAI,uBAAuB;IAIjC,YAAY,IAAI,eAAe,EAAE;IAOjC,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IA0BzE,KAAK,IAAI,QAAQ,CAAC,sBAAsB,CAAC;IAMzC,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,cAAc;CAsCvB"}
|