@carverjs/multiplayer 0.0.1 → 0.0.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 +154 -0
- package/dist/InputBuffer-J6XT_Tt0.d.mts +61 -0
- package/dist/InputBuffer-V7XfHbc6.d.ts +61 -0
- package/dist/{NetworkManager-nvVAOr1O.d.ts → NetworkManager-D-DxFgdM.d.mts} +66 -14
- package/dist/{NetworkManager-DrKM2tEx.d.mts → NetworkManager-DH9uGVMg.d.ts} +66 -14
- package/dist/{chunk-UD6FDZMX.mjs → chunk-GOTAQDBJ.mjs} +47 -4
- package/dist/chunk-GOTAQDBJ.mjs.map +1 -0
- package/dist/{chunk-3KT73N2S.mjs → chunk-LPNEP2VH.mjs} +0 -0
- package/dist/chunk-LPNEP2VH.mjs.map +1 -0
- package/dist/{chunk-EO3YNPRQ.mjs → chunk-Q25TJEY4.mjs} +494 -204
- package/dist/chunk-Q25TJEY4.mjs.map +1 -0
- package/dist/{firebase-CPu87KA0.d.ts → firebase-B5MgLlHk.d.ts} +6 -1
- package/dist/{firebase-PE6MxGdJ.d.mts → firebase-GrbVrNgs.d.mts} +6 -1
- package/dist/index.d.mts +27 -6
- package/dist/index.d.ts +27 -6
- package/dist/index.js +821 -258
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +172 -37
- package/dist/index.mjs.map +1 -1
- package/dist/strategy.d.mts +2 -2
- package/dist/strategy.d.ts +2 -2
- package/dist/strategy.js +46 -3
- package/dist/strategy.js.map +1 -1
- package/dist/strategy.mjs +1 -1
- package/dist/sync.d.mts +134 -50
- package/dist/sync.d.ts +134 -50
- package/dist/sync.js +499 -205
- package/dist/sync.js.map +1 -1
- package/dist/sync.mjs +15 -3
- package/dist/transport.d.mts +0 -0
- package/dist/transport.d.ts +0 -0
- package/dist/transport.js +0 -0
- package/dist/transport.js.map +1 -1
- package/dist/transport.mjs +2 -2
- package/dist/{types-5LHBOW08.d.mts → types-hNfCIBzj.d.mts} +7 -0
- package/dist/{types-5LHBOW08.d.ts → types-hNfCIBzj.d.ts} +7 -0
- package/dist/types.d.mts +2 -2
- package/dist/types.d.ts +2 -2
- package/dist/types.js.map +1 -1
- package/package.json +26 -5
- package/dist/chunk-3KT73N2S.mjs.map +0 -1
- package/dist/chunk-EO3YNPRQ.mjs.map +0 -1
- package/dist/chunk-UD6FDZMX.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/sync/EventSync.ts","../src/core/HostAuthority.ts","../src/core/ClientReceiver.ts","../src/sync/SnapshotSync.ts","../src/core/InputBuffer.ts","../src/core/InputUtils.ts","../src/sync/Rollback.ts","../src/sync/PredictionSync.ts"],"sourcesContent":["import type { CarverTransport, CarverChannel, EventPacket } from \"../types\";\n\n/**\n * Layer 1: Event-based messaging over a reliable+ordered channel.\n * Used for turn-based games, chat, and infrequent state changes.\n */\nexport class EventSync {\n private _transport: CarverTransport;\n private _channel: CarverChannel<string>;\n private _handlers = new Map<string, ((payload: unknown, peerId: string) => void)[]>();\n private _hostValidation: boolean;\n\n constructor(transport: CarverTransport, options?: { hostValidation?: boolean }) {\n this._transport = transport;\n this._hostValidation = options?.hostValidation ?? false;\n\n // Create a single reliable+ordered channel for events\n this._channel = transport.createChannel<string>('carver:events', {\n reliable: true,\n ordered: true,\n });\n\n // Listen for incoming events\n this._channel.onReceive((rawData: string, peerId: string) => {\n try {\n const packet: EventPacket = typeof rawData === 'string' ? JSON.parse(rawData) : rawData as unknown as EventPacket;\n\n // If host validation is enabled and we're the host, rebroadcast\n if (this._hostValidation && this._transport.isHost && packet.sender !== this._transport.peerId) {\n // Rebroadcast to all peers (except the original sender)\n const targets = Array.from(this._transport.peers).filter(p => p !== peerId);\n if (targets.length > 0) {\n this._channel.send(JSON.stringify(packet), targets);\n }\n }\n\n // If host validation is enabled and we're NOT the host,\n // only accept events from host (who rebroadcasts validated events)\n if (this._hostValidation && !this._transport.isHost && peerId !== this._transport.hostId) {\n return;\n }\n\n // Fire handlers for this event type\n const handlers = this._handlers.get(packet.type);\n if (handlers) {\n for (const handler of handlers) {\n handler(packet.payload, packet.sender);\n }\n }\n } catch {\n // Ignore malformed events\n }\n });\n }\n\n /**\n * Send a typed event to a specific peer or all peers.\n */\n sendEvent(type: string, payload: unknown, target?: string): void {\n const packet: EventPacket = {\n type,\n payload,\n sender: this._transport.peerId,\n target,\n };\n const serialized = JSON.stringify(packet);\n\n if (this._hostValidation && !this._transport.isHost) {\n // Route through host for validation\n this._channel.send(serialized, this._transport.hostId);\n } else if (target) {\n this._channel.send(serialized, target);\n } else {\n // Broadcast to all peers\n this._channel.send(serialized);\n }\n }\n\n /**\n * Broadcast a typed event to all connected peers.\n */\n broadcast(type: string, payload: unknown): void {\n this.sendEvent(type, payload);\n }\n\n /**\n * Register a handler for a specific event type.\n * Returns an unsubscribe function.\n */\n onEvent(type: string, callback: (payload: unknown, peerId: string) => void): () => void {\n let handlers = this._handlers.get(type);\n if (!handlers) {\n handlers = [];\n this._handlers.set(type, handlers);\n }\n handlers.push(callback);\n\n return () => {\n const arr = this._handlers.get(type);\n if (arr) {\n const idx = arr.indexOf(callback);\n if (idx >= 0) arr.splice(idx, 1);\n if (arr.length === 0) this._handlers.delete(type);\n }\n };\n }\n\n /**\n * Clean up the event channel.\n */\n destroy(): void {\n this._channel.close();\n this._handlers.clear();\n }\n}\n","import type {\n CarverTransport,\n CarverChannel,\n EntityState,\n PlayerInput,\n} from \"../types\";\nimport { Codec, SnapshotBuffer } from \"./codec\";\n\n/**\n * Host-side authority: reads networked actor states, serializes with delta compression,\n * and broadcasts to all clients at the configured broadcast rate.\n */\nexport class HostAuthority {\n private _transport: CarverTransport;\n private _codec: Codec;\n private _snapshotBuffer: SnapshotBuffer;\n private _snapshotChannel: CarverChannel<Uint8Array>;\n private _ackChannel: CarverChannel<string>;\n private _tick = 0;\n private _broadcastRate: number;\n private _broadcastAccumulator = 0;\n private _keyframeInterval: number;\n\n // Per-client last ACK'd tick for delta compression\n private _clientBaselines = new Map<string, number>();\n // Per-client last keyframe tick for scheduling\n private _clientLastKeyframeTick = new Map<string, number>();\n\n // Interest management callback (optional)\n private _interestFilter:\n | ((entityId: string, peerId: string) => boolean)\n | null = null;\n\n constructor(\n transport: CarverTransport,\n codec: Codec,\n snapshotBuffer: SnapshotBuffer,\n options?: {\n broadcastRate?: number;\n keyframeInterval?: number;\n },\n ) {\n this._transport = transport;\n this._codec = codec;\n this._snapshotBuffer = snapshotBuffer;\n this._broadcastRate = options?.broadcastRate ?? 20;\n this._keyframeInterval = options?.keyframeInterval ?? 300;\n\n // Unreliable channel for snapshots (fast, may drop)\n this._snapshotChannel = transport.createChannel<Uint8Array>(\n \"carver:snapshots\",\n {\n reliable: false,\n ordered: false,\n maxRetransmits: 0,\n },\n );\n\n // Reliable channel for ACKs\n this._ackChannel = transport.createChannel<string>(\"carver:acks\", {\n reliable: true,\n ordered: true,\n });\n\n // Listen for client ACKs\n this._ackChannel.onReceive((data: string, peerId: string) => {\n try {\n const ackTick =\n typeof data === \"string\"\n ? parseInt(data, 10)\n : (data as unknown as number);\n if (ackTick === -1) {\n // Client requesting keyframe\n this._clientBaselines.delete(peerId);\n } else {\n this._clientBaselines.set(peerId, ackTick);\n }\n } catch {\n /* ignore malformed ACKs */\n }\n });\n\n // Handle new peer joins — they need a keyframe\n transport.onPeerJoin((peerId) => {\n this._clientBaselines.delete(peerId); // Will force keyframe\n });\n\n transport.onPeerLeave((peerId) => {\n this._clientBaselines.delete(peerId);\n });\n }\n\n /** Set optional interest management filter */\n setInterestFilter(\n filter: ((entityId: string, peerId: string) => boolean) | null,\n ): void {\n this._interestFilter = filter;\n }\n\n /**\n * Called every fixed tick by the sync engine.\n * Collects entity states and decides whether to broadcast.\n * `hostInput` (prediction mode) is embedded in the snapshot packet as `hi`.\n */\n tick(\n currentTick: number,\n entities: Map<string, EntityState>,\n delta: number,\n hostInput?: PlayerInput,\n ): void {\n this._tick = currentTick;\n\n // Store snapshot in ring buffer\n this._snapshotBuffer.store(currentTick, new Map(entities));\n\n // Check if we should broadcast this tick\n this._broadcastAccumulator += delta;\n const broadcastInterval = 1 / this._broadcastRate;\n if (this._broadcastAccumulator < broadcastInterval) return;\n this._broadcastAccumulator -= broadcastInterval;\n\n // Broadcast to each connected client\n for (const peerId of this._transport.peers) {\n this._broadcastToClient(peerId, currentTick, entities, hostInput);\n }\n }\n\n /** Force a keyframe broadcast to all clients (e.g., after host migration) */\n forceKeyframe(\n currentTick: number,\n entities: Map<string, EntityState>,\n hostInput?: PlayerInput,\n ): void {\n this._clientBaselines.clear();\n this._clientLastKeyframeTick.clear();\n this._snapshotBuffer.store(currentTick, new Map(entities));\n for (const peerId of this._transport.peers) {\n this._broadcastToClient(peerId, currentTick, entities, hostInput);\n }\n }\n\n destroy(): void {\n this._snapshotChannel.close();\n this._ackChannel.close();\n this._clientBaselines.clear();\n this._clientLastKeyframeTick.clear();\n }\n\n private _broadcastToClient(\n peerId: string,\n currentTick: number,\n entities: Map<string, EntityState>,\n hostInput?: PlayerInput,\n ): void {\n // Apply interest management filter\n let clientEntities = entities;\n if (this._interestFilter) {\n clientEntities = new Map<string, EntityState>();\n for (const [id, entity] of entities) {\n if (this._interestFilter(id, peerId)) {\n clientEntities.set(id, entity);\n }\n }\n }\n\n // Determine baseline for delta compression (per-client keyframe scheduling)\n const clientBaseTick = this._clientBaselines.get(peerId);\n const clientLastKeyframe = this._clientLastKeyframeTick.get(peerId) ?? 0;\n const needsKeyframe =\n clientBaseTick === undefined ||\n currentTick - clientLastKeyframe >= this._keyframeInterval;\n\n let baseline: Map<string, EntityState> | undefined;\n if (!needsKeyframe && clientBaseTick !== undefined) {\n baseline = this._snapshotBuffer.get(clientBaseTick);\n }\n\n if (needsKeyframe) {\n this._clientLastKeyframeTick.set(peerId, currentTick);\n }\n\n // Serialize (delta or keyframe)\n const packet = this._codec.serializeDelta(\n currentTick,\n needsKeyframe ? -1 : (clientBaseTick ?? -1),\n clientEntities,\n baseline,\n hostInput,\n );\n\n if (packet) {\n this._snapshotChannel.send(packet, peerId);\n }\n }\n}\n","import type {\n CarverTransport,\n CarverChannel,\n EntityState,\n EntityState2D,\n EntityState3D,\n NetworkQuality,\n SnapshotListener,\n} from \"../types\";\nimport { Codec } from \"./codec\";\n\ninterface BufferedSnapshot {\n tick: number;\n entities: Map<string, EntityState>;\n receivedAt: number;\n}\n\n/**\n * Client-side state receiver: buffers incoming snapshots and interpolates\n * between them for smooth rendering.\n */\nexport class ClientReceiver {\n private _transport: CarverTransport;\n private _codec: Codec;\n private _snapshotChannel: CarverChannel<Uint8Array>;\n private _ackChannel: CarverChannel<string>;\n\n // Snapshot buffer (ring buffer of last N snapshots)\n private _buffer: BufferedSnapshot[] = [];\n private _bufferSize: number;\n\n // Interpolation settings\n private _method: \"hermite\" | \"linear\";\n private _extrapolateMs: number;\n\n // Current interpolated state\n private _interpolatedState = new Map<string, EntityState>();\n\n // Network quality tracking\n private _lastSnapshotTime = 0;\n private _networkQuality: NetworkQuality = \"good\";\n private _packetLossCount = 0;\n private _packetCount = 0;\n\n // Is2D mode\n private _is2D: boolean;\n\n // Entity state: full accumulated state from keyframes + deltas\n private _fullState = new Map<string, EntityState>();\n\n // Listeners fired after each snapshot is merged into the full world state\n private _snapshotListeners: SnapshotListener[] = [];\n\n constructor(\n transport: CarverTransport,\n codec: Codec,\n options?: {\n bufferSize?: number;\n method?: \"hermite\" | \"linear\";\n extrapolateMs?: number;\n is2D?: boolean;\n },\n ) {\n this._transport = transport;\n this._codec = codec;\n this._bufferSize = options?.bufferSize ?? 3;\n this._method = options?.method ?? \"hermite\";\n this._extrapolateMs = options?.extrapolateMs ?? 250;\n this._is2D = options?.is2D ?? false;\n\n // Listen on the unreliable snapshot channel\n this._snapshotChannel = transport.createChannel<Uint8Array>(\n \"carver:snapshots\",\n {\n reliable: false,\n ordered: false,\n maxRetransmits: 0,\n },\n );\n\n this._ackChannel = transport.createChannel<string>(\"carver:acks\", {\n reliable: true,\n ordered: true,\n });\n\n this._snapshotChannel.onReceive((data: Uint8Array) => {\n this._handleSnapshot(data);\n });\n }\n\n /** Get the current interpolated entity states */\n get state(): Map<string, EntityState> {\n return this._interpolatedState;\n }\n\n get networkQuality(): NetworkQuality {\n return this._networkQuality;\n }\n\n /**\n * Called every render frame to interpolate between buffered snapshots.\n * @param renderTime - current render time in ms\n */\n interpolate(renderTime: number): Map<string, EntityState> {\n if (this._buffer.length < 2) {\n // Not enough snapshots to interpolate, return latest\n return this._buffer.length > 0\n ? this._buffer[this._buffer.length - 1].entities\n : this._interpolatedState;\n }\n\n // Find two snapshots to interpolate between\n // We render \"behind\" by one buffer interval\n const interpDelay = (this._bufferSize - 1) * (1000 / 20); // Assume ~20 pps\n const targetTime = renderTime - interpDelay;\n\n let from: BufferedSnapshot | null = null;\n let to: BufferedSnapshot | null = null;\n\n for (let i = 0; i < this._buffer.length - 1; i++) {\n if (\n this._buffer[i].receivedAt <= targetTime &&\n this._buffer[i + 1].receivedAt > targetTime\n ) {\n from = this._buffer[i];\n to = this._buffer[i + 1];\n break;\n }\n }\n\n if (!from || !to) {\n // Extrapolation case: we're past all buffered snapshots\n const latest = this._buffer[this._buffer.length - 1];\n const timeSinceLatest = renderTime - latest.receivedAt;\n\n if (timeSinceLatest > this._extrapolateMs) {\n // Too far behind, just use latest snapshot\n this._updateNetworkQuality(\"poor\");\n return latest.entities;\n }\n\n if (this._buffer.length >= 2) {\n from = this._buffer[this._buffer.length - 2];\n to = latest;\n this._updateNetworkQuality(\"degraded\");\n } else {\n return latest.entities;\n }\n } else {\n this._updateNetworkQuality(\"good\");\n }\n\n // Compute interpolation factor\n const range = to.receivedAt - from.receivedAt;\n const t =\n range > 0\n ? Math.min(1, Math.max(0, (targetTime - from.receivedAt) / range))\n : 1;\n\n // Interpolate all entities\n const result = new Map<string, EntityState>();\n const allIds = new Set([...from.entities.keys(), ...to.entities.keys()]);\n\n for (const id of allIds) {\n const fromEntity = from.entities.get(id);\n const toEntity = to.entities.get(id);\n\n if (toEntity && toEntity.c?.__removed) continue; // Removed entity\n\n if (fromEntity && toEntity) {\n result.set(id, this._interpolateEntity(fromEntity, toEntity, t));\n } else if (toEntity) {\n result.set(id, toEntity);\n }\n }\n\n this._interpolatedState = result;\n return result;\n }\n\n /** Request a keyframe from the host */\n requestKeyframe(): void {\n this._ackChannel.send(\"-1\");\n }\n\n /** Register a listener fired after each snapshot is merged into the full world state */\n onSnapshot(cb: SnapshotListener): void {\n this._snapshotListeners.push(cb);\n }\n\n destroy(): void {\n this._snapshotChannel.close();\n this._ackChannel.close();\n this._buffer = [];\n this._interpolatedState.clear();\n this._fullState.clear();\n this._snapshotListeners = [];\n }\n\n private _handleSnapshot(data: Uint8Array): void {\n try {\n const { tick, baseTick, entities, hostInput } =\n this._codec.deserializePacket(data);\n const now = performance.now();\n\n if (baseTick === -1) {\n // Keyframe: replace full state\n this._fullState.clear();\n for (const entity of entities) {\n this._fullState.set(entity.id, entity);\n }\n } else {\n // Delta: apply changes to full state\n for (const entity of entities) {\n if (entity.c?.__removed) {\n this._fullState.delete(entity.id);\n } else {\n this._fullState.set(entity.id, entity);\n }\n }\n }\n\n // Buffer the full state snapshot\n const fullStateClone = new Map(this._fullState);\n this._buffer.push({\n tick,\n entities: fullStateClone,\n receivedAt: now,\n });\n\n // Keep buffer bounded\n while (this._buffer.length > this._bufferSize * 2) {\n this._buffer.shift();\n }\n\n // Send ACK\n this._ackChannel.send(String(tick));\n\n // Notify snapshot listeners with the merged full world state\n for (const cb of this._snapshotListeners) {\n cb(tick, fullStateClone, hostInput);\n }\n\n // Track timing for network quality\n this._lastSnapshotTime = now;\n this._packetCount++;\n } catch {\n this._packetLossCount++;\n }\n }\n\n private _interpolateEntity(\n from: EntityState,\n to: EntityState,\n t: number,\n ): EntityState {\n if (this._is2D || !(\"z\" in from)) {\n return this._interpolateEntity2D(\n from as EntityState2D,\n to as EntityState2D,\n t,\n );\n }\n return this._interpolateEntity3D(\n from as EntityState3D,\n to as EntityState3D,\n t,\n );\n }\n\n private _interpolateEntity2D(\n from: EntityState2D,\n to: EntityState2D,\n t: number,\n ): EntityState2D {\n if (this._method === \"hermite\") {\n return {\n id: to.id,\n x: hermite(from.x, from.vx, to.x, to.vx, t),\n y: hermite(from.y, from.vy, to.y, to.vy, t),\n a: lerpAngle(from.a, to.a, t),\n vx: lerp(from.vx, to.vx, t),\n vy: lerp(from.vy, to.vy, t),\n va: lerp(from.va, to.va, t),\n c: interpolateCustom(from.c, to.c, t),\n };\n }\n // Linear\n return {\n id: to.id,\n x: lerp(from.x, to.x, t),\n y: lerp(from.y, to.y, t),\n a: lerpAngle(from.a, to.a, t),\n vx: lerp(from.vx, to.vx, t),\n vy: lerp(from.vy, to.vy, t),\n va: lerp(from.va, to.va, t),\n c: interpolateCustom(from.c, to.c, t),\n };\n }\n\n private _interpolateEntity3D(\n from: EntityState3D,\n to: EntityState3D,\n t: number,\n ): EntityState3D {\n // Quaternion SLERP for rotation\n const [qx, qy, qz, qw] = slerp(\n from.qx,\n from.qy,\n from.qz,\n from.qw,\n to.qx,\n to.qy,\n to.qz,\n to.qw,\n t,\n );\n\n if (this._method === \"hermite\") {\n return {\n id: to.id,\n x: hermite(from.x, from.vx, to.x, to.vx, t),\n y: hermite(from.y, from.vy, to.y, to.vy, t),\n z: hermite(from.z, from.vz, to.z, to.vz, t),\n qx,\n qy,\n qz,\n qw,\n vx: lerp(from.vx, to.vx, t),\n vy: lerp(from.vy, to.vy, t),\n vz: lerp(from.vz, to.vz, t),\n wx: lerp(from.wx, to.wx, t),\n wy: lerp(from.wy, to.wy, t),\n wz: lerp(from.wz, to.wz, t),\n c: interpolateCustom(from.c, to.c, t),\n };\n }\n\n // Linear\n return {\n id: to.id,\n x: lerp(from.x, to.x, t),\n y: lerp(from.y, to.y, t),\n z: lerp(from.z, to.z, t),\n qx,\n qy,\n qz,\n qw,\n vx: lerp(from.vx, to.vx, t),\n vy: lerp(from.vy, to.vy, t),\n vz: lerp(from.vz, to.vz, t),\n wx: lerp(from.wx, to.wx, t),\n wy: lerp(from.wy, to.wy, t),\n wz: lerp(from.wz, to.wz, t),\n c: interpolateCustom(from.c, to.c, t),\n };\n }\n\n private _updateNetworkQuality(quality: NetworkQuality): void {\n this._networkQuality = quality;\n }\n}\n\n// ── Math utilities ──\n\nfunction lerp(a: number, b: number, t: number): number {\n return a + (b - a) * t;\n}\n\nfunction lerpAngle(a: number, b: number, t: number): number {\n let diff = b - a;\n // Wrap to [-PI, PI]\n while (diff > Math.PI) diff -= Math.PI * 2;\n while (diff < -Math.PI) diff += Math.PI * 2;\n return a + diff * t;\n}\n\n/**\n * Hermite spline interpolation using velocity for smooth curves.\n * h(t) = (2t^3 - 3t^2 + 1)p0 + (t^3 - 2t^2 + t)v0 + (-2t^3 + 3t^2)p1 + (t^3 - t^2)v1\n */\nfunction hermite(\n p0: number,\n v0: number,\n p1: number,\n v1: number,\n t: number,\n): number {\n const t2 = t * t;\n const t3 = t2 * t;\n return (\n (2 * t3 - 3 * t2 + 1) * p0 +\n (t3 - 2 * t2 + t) * v0 +\n (-2 * t3 + 3 * t2) * p1 +\n (t3 - t2) * v1\n );\n}\n\n/**\n * Quaternion SLERP (Spherical Linear Interpolation).\n */\nfunction slerp(\n ax: number,\n ay: number,\n az: number,\n aw: number,\n bx: number,\n by: number,\n bz: number,\n bw: number,\n t: number,\n): [number, number, number, number] {\n // Compute dot product\n let dot = ax * bx + ay * by + az * bz + aw * bw;\n\n // Ensure shortest path\n if (dot < 0) {\n bx = -bx;\n by = -by;\n bz = -bz;\n bw = -bw;\n dot = -dot;\n }\n\n if (dot > 0.9995) {\n // Very close, use linear interpolation\n return [lerp(ax, bx, t), lerp(ay, by, t), lerp(az, bz, t), lerp(aw, bw, t)];\n }\n\n const theta = Math.acos(Math.min(1, Math.max(-1, dot)));\n const sinTheta = Math.sin(theta);\n const wa = Math.sin((1 - t) * theta) / sinTheta;\n const wb = Math.sin(t * theta) / sinTheta;\n\n return [\n ax * wa + bx * wb,\n ay * wa + by * wb,\n az * wa + bz * wb,\n aw * wa + bw * wb,\n ];\n}\n\n/**\n * Interpolate custom properties: lerp numbers, instant-swap others.\n */\nfunction interpolateCustom(\n from: Record<string, unknown> | undefined,\n to: Record<string, unknown> | undefined,\n t: number,\n): Record<string, unknown> | undefined {\n if (!from && !to) return undefined;\n if (!from) return to;\n if (!to) return from;\n\n const result: Record<string, unknown> = {};\n const allKeys = new Set([...Object.keys(from), ...Object.keys(to)]);\n\n for (const key of allKeys) {\n const fromVal = from[key];\n const toVal = to[key];\n if (typeof fromVal === \"number\" && typeof toVal === \"number\") {\n result[key] = lerp(fromVal, toVal, t);\n } else {\n // Instant swap: use target value after halfway\n result[key] = t >= 0.5 ? toVal : fromVal;\n }\n }\n\n return result;\n}\n","import type {\n CarverTransport,\n EntityState,\n PlayerInput,\n SnapshotListener,\n SnapshotSource,\n} from \"../types\";\nimport { Codec, SnapshotBuffer } from \"../core/codec\";\nimport { HostAuthority } from \"../core/HostAuthority\";\nimport { ClientReceiver } from \"../core/ClientReceiver\";\n\nexport interface SnapshotSyncOptions {\n broadcastRate?: number;\n keyframeInterval?: number;\n bufferSize?: number;\n interpolationMethod?: \"hermite\" | \"linear\";\n extrapolateMs?: number;\n is2D?: boolean;\n}\n\n/**\n * Layer 2: Snapshot interpolation sync engine.\n * Host broadcasts state at fixed intervals, clients interpolate.\n */\nexport class SnapshotSync implements SnapshotSource {\n private _transport: CarverTransport;\n private _hostAuthority: HostAuthority | null = null;\n private _clientReceiver: ClientReceiver | null = null;\n private _codec: Codec;\n private _snapshotBuffer: SnapshotBuffer;\n\n // Snapshot listeners survive host migration (forwarder re-attached on demote)\n private _snapshotListeners: SnapshotListener[] = [];\n\n constructor(\n transport: CarverTransport,\n codec: Codec,\n snapshotBuffer: SnapshotBuffer,\n options?: SnapshotSyncOptions,\n ) {\n this._transport = transport;\n this._codec = codec;\n this._snapshotBuffer = snapshotBuffer;\n\n if (transport.isHost) {\n this._hostAuthority = new HostAuthority(\n transport,\n codec,\n snapshotBuffer,\n {\n broadcastRate: options?.broadcastRate,\n keyframeInterval: options?.keyframeInterval,\n },\n );\n } else {\n this._clientReceiver = new ClientReceiver(transport, codec, {\n bufferSize: options?.bufferSize,\n method: options?.interpolationMethod,\n extrapolateMs: options?.extrapolateMs,\n is2D: options?.is2D,\n });\n this._attachSnapshotForwarder(this._clientReceiver);\n }\n }\n\n get isHost(): boolean {\n return this._hostAuthority !== null;\n }\n\n get hostAuthority(): HostAuthority | null {\n return this._hostAuthority;\n }\n\n get clientReceiver(): ClientReceiver | null {\n return this._clientReceiver;\n }\n\n /** Host: called every fixed tick to potentially broadcast state */\n hostTick(\n tick: number,\n entities: Map<string, EntityState>,\n delta: number,\n hostInput?: PlayerInput,\n ): void {\n this._hostAuthority?.tick(tick, entities, delta, hostInput);\n }\n\n /** Register a listener fired after each merged snapshot (client side). */\n onSnapshot(cb: SnapshotListener): void {\n this._snapshotListeners.push(cb);\n }\n\n /** Client: called every render frame to interpolate */\n clientInterpolate(renderTime: number): Map<string, EntityState> {\n return this._clientReceiver?.interpolate(renderTime) ?? new Map();\n }\n\n /** Set interest filter on host authority */\n setInterestFilter(\n filter: ((entityId: string, peerId: string) => boolean) | null,\n ): void {\n this._hostAuthority?.setInterestFilter(filter);\n }\n\n /** Handle host migration: switch from client to host mode */\n promoteToHost(options?: SnapshotSyncOptions): void {\n this._clientReceiver?.destroy();\n this._clientReceiver = null;\n this._hostAuthority = new HostAuthority(\n this._transport,\n this._codec,\n this._snapshotBuffer,\n {\n broadcastRate: options?.broadcastRate,\n keyframeInterval: options?.keyframeInterval,\n },\n );\n }\n\n /** Handle host migration: switch from host to client mode */\n demoteToClient(options?: SnapshotSyncOptions): void {\n this._hostAuthority?.destroy();\n this._hostAuthority = null;\n this._clientReceiver = new ClientReceiver(\n this._transport,\n this._codec,\n {\n bufferSize: options?.bufferSize,\n method: options?.interpolationMethod,\n extrapolateMs: options?.extrapolateMs,\n is2D: options?.is2D,\n },\n );\n this._attachSnapshotForwarder(this._clientReceiver);\n }\n\n destroy(): void {\n this._hostAuthority?.destroy();\n this._clientReceiver?.destroy();\n this._hostAuthority = null;\n this._clientReceiver = null;\n this._snapshotListeners = [];\n }\n\n // ── Private ──\n\n /** Forward receiver snapshots to registered listeners (survives host migration). */\n private _attachSnapshotForwarder(receiver: ClientReceiver): void {\n receiver.onSnapshot((t, e, hi) => {\n for (const l of this._snapshotListeners) l(t, e, hi);\n });\n }\n}\n","/**\n * InputBuffer — unified ring-buffer for local and peer inputs.\n *\n * Ported from LumberNet's LumberInputBuffer. Stores:\n * - local player tick-keyed inputs (storeTick / getTick / hasTick)\n * - last-received input per remote peer (setRemote / getRemote / allRemotes)\n * - per-peer tick-keyed inputs for accurate rollback resimulation (getRemoteAtTick)\n *\n * Generic over per-tick payload I. Caller supplies the neutral payload.\n */\n\nimport type { PlayerInput } from \"../types\";\n\nexport class InputBuffer<I extends PlayerInput = PlayerInput> {\n private readonly _historySize: number;\n private readonly _neutral: I;\n\n /** Local player tick-keyed inputs (ring buffer). */\n private readonly _local = new Map<number, I>();\n\n /** Last-received input per remote peer. */\n private readonly _remotes = new Map<string, I>();\n\n /** Per-peer tick-keyed inputs for accurate rollback re-simulation. */\n private readonly _peerTicks = new Map<string, Map<number, I>>();\n\n constructor(neutralInput: I, historySize = 120) {\n this._neutral = { ...neutralInput } as I;\n this._historySize = historySize;\n }\n\n // ── Local input ──\n\n /** Record a snapshot of the local input at the given tick. Evicts the entry exactly historySize back. */\n storeTick(tick: number, input: I): void {\n this._local.set(tick, { ...input } as I);\n this._local.delete(tick - this._historySize);\n }\n\n /** Return the local input at `tick`, or a neutral copy if out of range. */\n getTick(tick: number): I {\n return this._local.get(tick) ?? ({ ...this._neutral } as I);\n }\n\n /** True if we have a stored local input for this tick (used to avoid spurious justPressed after snap/rejoin). */\n hasTick(tick: number): boolean {\n return this._local.has(tick);\n }\n\n /** Neutral payload with every boolean field forced false (use for justPressed when prev tick is unknown). */\n getJustPressedZero(): I {\n const out = {} as I;\n for (const key in this._neutral) {\n const v = this._neutral[key];\n (out as Record<string, boolean | number | undefined>)[key] =\n typeof v === \"boolean\" ? false : v;\n }\n return out;\n }\n\n // ── Remote (peer) inputs ──\n\n /**\n * Record a remote peer's input. If `tick` is given (the sender's local tick),\n * also store it in the per-peer ring buffer for rollback.\n */\n setRemote(peerId: string, input: I, tick?: number): void {\n this._remotes.set(peerId, input);\n if (tick !== undefined) {\n let peerMap = this._peerTicks.get(peerId);\n if (!peerMap) {\n peerMap = new Map();\n this._peerTicks.set(peerId, peerMap);\n }\n peerMap.set(tick, input);\n peerMap.delete(tick - this._historySize);\n }\n }\n\n /** Last-known input for a peer, or a neutral copy if never received. */\n getRemote(peerId: string): I {\n return this._remotes.get(peerId) ?? ({ ...this._neutral } as I);\n }\n\n /** Snapshot of all remote peers' last-known inputs (shallow copy of the map). */\n allRemotes(): Map<string, I> {\n return new Map(this._remotes);\n }\n\n /**\n * Return a peer's exact input at the given tick (for rollback accuracy),\n * falling back to their last-known input when history does not reach that far.\n */\n getRemoteAtTick(peerId: string, tick: number): I {\n return this._peerTicks.get(peerId)?.get(tick) ?? this.getRemote(peerId);\n }\n\n /** Override the last-known input for a peer (does NOT touch tick history). */\n overrideRemote(peerId: string, input: I): void {\n this._remotes.set(peerId, input);\n }\n\n /** Number of currently tracked remote peers. */\n get peerCount(): number {\n return this._remotes.size;\n }\n\n /** Iterate tracked peer IDs. */\n peerIds(): IterableIterator<string> {\n return this._remotes.keys();\n }\n\n /**\n * Keep only these peer IDs; remove any other remotes (e.g. after leave/rejoin).\n * Call when the room's peer list changes so stale peers stop receiving input.\n */\n setPeerIds(peerIds: ReadonlySet<string> | string[]): void {\n const set = peerIds instanceof Set ? peerIds : new Set(peerIds);\n for (const id of this._remotes.keys()) {\n if (!set.has(id)) {\n this._remotes.delete(id);\n this._peerTicks.delete(id);\n }\n }\n }\n\n // ── Lifecycle ──\n\n /** Clear local history, remote last-known inputs, and per-peer tick history. */\n clear(): void {\n this._local.clear();\n this._remotes.clear();\n this._peerTicks.clear();\n }\n}\n","/**\n * Input utilities for prediction mode.\n * Ported from LumberNet's InputUtils.\n */\n\nimport type { PlayerInput } from \"../types\";\n\n/**\n * Given the input state at the current tick and the previous tick, return a new\n * input object where each boolean field is `true` only if it transitioned\n * false -> true (a \"rising edge\" / \"just pressed\").\n *\n * Non-boolean fields are passed through unchanged from `curr`.\n * Iterates `curr` keys only; keys present only in `prev` are absent from the result.\n *\n * PredictionSync calls this automatically and passes the result as `justPressed`\n * to every onPhysicsStep callback, so games normally do not call it directly.\n */\nexport function computeJustPressed<I extends PlayerInput>(curr: I, prev: I): I {\n const out = {} as I;\n for (const key in curr) {\n const c = curr[key];\n (out as Record<string, boolean | number | undefined>)[key] =\n typeof c === \"boolean\" ? c === true && prev[key] !== true : c;\n }\n return out;\n}\n","/**\n * Rollback — full-world rollback for prediction mode.\n *\n * Ported from LumberNet's LumberRollback, adapted to CarverJS entity state\n * (2D and 3D). On each accepted server snapshot the client:\n * 1. captures the pre-rollback visual pose (raw physics + accumulated error),\n * 2. hard-applies server state to EVERY networked entity,\n * 3. resimulates from serverTick + 1 to localTick replaying per-tick inputs\n * (or hard-snaps the tick when drift exceeds maxRewindTicks),\n * 4. converts the visual discontinuity into per-entity error offsets.\n *\n * Pure module: no transport, no React, no three.js. Quaternion math is\n * hand-rolled below.\n */\n\nimport type { InputBuffer } from \"../core/InputBuffer\";\nimport { computeJustPressed } from \"../core/InputUtils\";\nimport type {\n EntityState,\n ErrorOffset,\n PhysicsStepCallback,\n PlayerInput,\n PredictionWorldDriver,\n} from \"../types\";\n\n// ── Quaternion helpers ──\n\nexport interface Quat {\n x: number;\n y: number;\n z: number;\n w: number;\n}\n\nconst IDENTITY_QUAT: Quat = { x: 0, y: 0, z: 0, w: 1 };\n\n/** Hamilton product (a ⊗ b). */\nexport function quatMultiply(a: Quat, b: Quat): Quat {\n return {\n x: a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,\n y: a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,\n z: a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,\n w: a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z,\n };\n}\n\n/** Conjugate (inputs assumed normalized). */\nexport function quatInvert(q: Quat): Quat {\n return { x: -q.x, y: -q.y, z: -q.z, w: q.w };\n}\n\n/** Normalize; degenerate (near-zero) quaternions return identity. */\nexport function quatNormalize(q: Quat): Quat {\n const mag = Math.hypot(q.x, q.y, q.z, q.w);\n if (mag < 1e-12) return { ...IDENTITY_QUAT };\n return { x: q.x / mag, y: q.y / mag, z: q.z / mag, w: q.w / mag };\n}\n\n/** Rotation angle in radians: 2*acos(clamp(|w|, 0, 1)). */\nexport function quatAngle(q: Quat): number {\n return 2 * Math.acos(Math.min(1, Math.abs(q.w)));\n}\n\n/** Scale the rotation angle toward identity by `factor`, preserving the axis. */\nexport function quatScaleAngle(q: Quat, factor: number): Quat {\n let n = quatNormalize(q);\n if (n.w < 0) n = { x: -n.x, y: -n.y, z: -n.z, w: -n.w };\n const angle = 2 * Math.acos(Math.min(1, n.w));\n if (angle < 1e-6) return { ...IDENTITY_QUAT };\n const s = Math.sin(angle / 2);\n const ax = n.x / s;\n const ay = n.y / s;\n const az = n.z / s;\n const na = angle * factor;\n const ns = Math.sin(na / 2);\n return { x: ax * ns, y: ay * ns, z: az * ns, w: Math.cos(na / 2) };\n}\n\n// ── Rollback ──\n\nexport interface RollbackParams {\n /** Tick embedded in the accepted server snapshot. */\n serverTick: number;\n /** Authoritative full-world state from the snapshot. */\n serverState: ReadonlyMap<string, EntityState>;\n /** Client's current simulation tick. */\n localTick: number;\n /** This peer's id (local inputs are replayed from the local ring buffer). */\n localPeerId: string;\n /** Input buffer holding local and per-peer tick history. */\n inputs: InputBuffer;\n /** Currently accumulated visual error offsets (kept continuous across rollbacks). */\n currentErrors: ReadonlyMap<string, ErrorOffset>;\n /** World access for capture/apply/step. */\n driver: PredictionWorldDriver;\n /** Game physics-step callback, re-invoked during resimulation. */\n callback: PhysicsStepCallback | null;\n /** Fixed timestep in seconds. */\n dt: number;\n /** Snap target offset: snap target = serverTick + driftTargetTicks. */\n driftTargetTicks: number;\n /** Max |localTick - (serverTick + driftTargetTicks)| before hard tick snap. */\n maxRewindTicks: number;\n /** Per-axis positional jump above which correction is suppressed (teleport). */\n snapThreshold: number;\n}\n\nexport interface RollbackResult {\n /** New local tick (differs from localTick only when a hard snap occurred). */\n newLocalTick: number;\n snapped: boolean;\n /** Per-entity visual error offsets (pre-visual minus post-resim). */\n errors: Map<string, ErrorOffset>;\n}\n\ninterface PreVisualPose {\n x: number;\n y: number;\n z: number;\n a: number;\n q: Quat;\n is3D: boolean;\n}\n\n/**\n * Apply a server snapshot to the whole world and resimulate forward.\n * Returns the new local tick and per-entity visual error offsets.\n */\nexport function applyRollback(params: RollbackParams): RollbackResult {\n const {\n serverTick,\n serverState,\n localTick,\n localPeerId,\n inputs,\n currentErrors,\n driver,\n callback,\n dt,\n driftTargetTicks,\n maxRewindTicks,\n snapThreshold,\n } = params;\n\n // Step 0: pre-rollback visual pose (raw physics + accumulated error)\n const preState = driver.captureState();\n const preMap = new Map<string, PreVisualPose>();\n for (const [id, s] of preState) {\n const e = currentErrors.get(id);\n const ex = e?.x ?? 0;\n const ey = e?.y ?? 0;\n if (\"z\" in s) {\n const errQ: Quat = e\n ? { x: e.qx, y: e.qy, z: e.qz, w: e.qw }\n : { ...IDENTITY_QUAT };\n preMap.set(id, {\n x: s.x + ex,\n y: s.y + ey,\n z: s.z + (e?.z ?? 0),\n a: 0,\n q: quatMultiply(errQ, { x: s.qx, y: s.qy, z: s.qz, w: s.qw }),\n is3D: true,\n });\n } else {\n preMap.set(id, {\n x: s.x + ex,\n y: s.y + ey,\n z: 0,\n a: s.a + (e?.a ?? 0),\n q: { ...IDENTITY_QUAT },\n is3D: false,\n });\n }\n }\n\n // Step 1: hard-apply server state to the WHOLE world (local player included)\n driver.applyState(serverState.values());\n\n // Step 2: snap-vs-resim decision\n const targetTick = serverTick + driftTargetTicks;\n const tickDiff = localTick - targetTick;\n let newLocalTick = localTick;\n let snapped = false;\n\n if (Math.abs(tickDiff) > maxRewindTicks) {\n newLocalTick = targetTick;\n snapped = true;\n } else {\n // Step 3: resimulate from serverTick + 1 to localTick replaying tick-exact inputs\n for (let i = serverTick + 1; i <= localTick; i++) {\n if (callback) {\n const tickInputs = new Map<string, PlayerInput>();\n const justPressed = new Map<string, PlayerInput>();\n for (const peerId of inputs.peerIds()) {\n if (peerId === localPeerId) continue;\n const curr = inputs.getRemoteAtTick(peerId, i);\n tickInputs.set(peerId, curr);\n justPressed.set(\n peerId,\n computeJustPressed(curr, inputs.getRemoteAtTick(peerId, i - 1)),\n );\n }\n const localCurr = inputs.getTick(i);\n tickInputs.set(localPeerId, localCurr);\n justPressed.set(\n localPeerId,\n computeJustPressed(localCurr, inputs.getTick(i - 1)),\n );\n callback(tickInputs, justPressed, i, true, dt);\n }\n driver.stepWorld?.();\n }\n }\n\n // Step 4: error vectors = pre-rollback visual pose minus post-resim state\n const postState = driver.captureState();\n const errors = new Map<string, ErrorOffset>();\n for (const [id, post] of postState) {\n const pre = preMap.get(id);\n if (!pre) continue;\n\n const errX = pre.x - post.x;\n const errY = pre.y - post.y;\n let errZ = 0;\n let errA = 0;\n let q: Quat = { ...IDENTITY_QUAT };\n\n if (\"z\" in post) {\n errZ = pre.z - post.z;\n let dq = quatNormalize(\n quatMultiply(\n pre.q,\n quatInvert({ x: post.qx, y: post.qy, z: post.qz, w: post.qw }),\n ),\n );\n if (dq.w < 0) dq = { x: -dq.x, y: -dq.y, z: -dq.z, w: -dq.w };\n q = dq;\n } else {\n const ad = pre.a - post.a;\n // Wrap to (-PI, PI]\n errA = ad - Math.PI * 2 * Math.floor((ad + Math.PI) / (Math.PI * 2));\n }\n\n // Large per-axis jump -> suppress ALL correction (intentional teleport)\n if (\n Math.abs(errX) > snapThreshold ||\n Math.abs(errY) > snapThreshold ||\n Math.abs(errZ) > snapThreshold\n ) {\n errors.set(id, { x: 0, y: 0, z: 0, a: 0, qx: 0, qy: 0, qz: 0, qw: 1 });\n } else {\n errors.set(id, {\n x: errX,\n y: errY,\n z: errZ,\n a: errA,\n qx: q.x,\n qy: q.y,\n qz: q.z,\n qw: q.w,\n });\n }\n }\n\n return { newLocalTick, snapped, errors };\n}\n","/**\n * Layer 3: Full-world prediction with full-world rollback.\n * Ported from LumberNet, built on top of Layer 2 (SnapshotSync) as the\n * authoritative state channel.\n *\n * Flow:\n * Every peer (host included): broadcast tick-stamped input to ALL peers each\n * fixed tick on carver:inputs, simulate EVERY networked entity forward with\n * last-known remote inputs (hold-last-input extrapolation).\n *\n * Host: stays authoritative; SnapshotSync broadcasts delta-compressed,\n * ACK-driven snapshots with the host's own input embedded (`hi`).\n *\n * Client: on each accepted snapshot, reset ALL networked entities to server\n * state, resimulate from serverTick + 1 to localTick replaying per-tick\n * inputs for every peer, and convert the visual discontinuity into\n * per-entity error offsets decayed per render frame.\n *\n * Role is checked dynamically via transport.isHost at every use site (host\n * migration is best-effort).\n */\n\nimport type {\n CarverTransport,\n CarverChannel,\n EntityState,\n ErrorOffset,\n InputPacket,\n PhysicsStepCallback,\n PlayerInput,\n PredictionSyncOptions,\n PredictionWorldDriver,\n SnapshotSource,\n} from \"../types\";\nimport { TickKeeper } from \"../core/TickKeeper\";\nimport { InputBuffer } from \"../core/InputBuffer\";\nimport { computeJustPressed } from \"../core/InputUtils\";\nimport { applyRollback, quatAngle, quatScaleAngle } from \"./Rollback\";\n\nconst DEFAULT_OPTIONS: Required<PredictionSyncOptions> = {\n maxRewindTicks: 15,\n snapThreshold: 150,\n errorDecay: 0.85,\n maxErrorPerFrame: 0,\n neutralInput: {},\n inputHistorySize: 120,\n driftTargetTicks: 4,\n};\n\ninterface PendingSnapshot {\n t: number;\n entities: Map<string, EntityState>;\n hostInput: PlayerInput | undefined;\n}\n\nexport class PredictionSync {\n private _transport: CarverTransport;\n private _tickKeeper: TickKeeper;\n private _options: Required<PredictionSyncOptions>;\n\n // Reliable ordered all-to-all input channel\n private _inputChannel: CarverChannel<InputPacket>;\n\n // Local + per-peer tick-stamped input history\n private _inputs: InputBuffer;\n\n // Local input; persists across ticks until replaced (hold-input semantics)\n private _currentInput: PlayerInput | null = null;\n\n // Newest pending server snapshot awaiting rollback (only the newest survives)\n private _pending: PendingSnapshot | null = null;\n\n // Per-entity accumulated visual error offsets\n private _errors = new Map<string, ErrorOffset>();\n\n private _serverTick = 0;\n private _lastAppliedServerTick = 0;\n\n private _worldDriver: PredictionWorldDriver | null = null;\n private _onPhysicsStep: PhysicsStepCallback | null = null;\n\n constructor(\n transport: CarverTransport,\n tickKeeper: TickKeeper,\n snapshots: SnapshotSource,\n options?: PredictionSyncOptions,\n ) {\n this._transport = transport;\n this._tickKeeper = tickKeeper;\n this._options = { ...DEFAULT_OPTIONS, ...options };\n\n this._inputs = new InputBuffer(\n this._options.neutralInput,\n this._options.inputHistorySize,\n );\n\n // All-to-all input broadcast channel (registered regardless of role)\n this._inputChannel = transport.createChannel<InputPacket>(\"carver:inputs\", {\n reliable: true,\n ordered: true,\n });\n\n this._inputChannel.onReceive(\n (data: InputPacket | string, peerId: string) => {\n try {\n const packet =\n typeof data === \"string\" ? (JSON.parse(data) as InputPacket) : data;\n if (\n typeof packet.t === \"number\" &&\n packet.i !== null &&\n typeof packet.i === \"object\"\n ) {\n // Key by the transport-provided sender id; never trust packet.p\n this._inputs.setRemote(peerId, packet.i, packet.t);\n }\n } catch (err) {\n if (typeof console !== \"undefined\")\n console.debug(\"[CarverJS] Malformed input packet:\", err);\n }\n },\n );\n\n // Accepted snapshots become pending rollbacks (clients only)\n snapshots.onSnapshot((tick, entities, hostInput) => {\n if (this._transport.isHost) return;\n if (tick <= this._lastAppliedServerTick) return;\n this._serverTick = tick;\n this._tickKeeper.setServerTick(tick);\n if (!this._pending || tick > this._pending.t) {\n this._pending = { t: tick, entities, hostInput };\n }\n });\n\n // Drop input state for departed peers\n transport.onPeerLeave(() => {\n this._inputs.setPeerIds(this._transport.peers);\n });\n }\n\n // ── Wiring ──\n\n /** Set the game simulation callback (forward sim + rollback resim). */\n setPhysicsStep(cb: PhysicsStepCallback): void {\n this._onPhysicsStep = cb;\n }\n\n /** Set the world driver used for forward stepping and rollback. */\n setWorldDriver(driver: PredictionWorldDriver): void {\n this._worldDriver = driver;\n }\n\n // ── Input ──\n\n /** Set the local player's input. PERSISTS across ticks until replaced. */\n setInput(input: PlayerInput): void {\n this._currentInput = input;\n }\n\n /** Local input stored at the given tick (neutral fallback). Used by the host to embed `hi`. */\n getLocalInput(tick: number): PlayerInput {\n return this._inputs.getTick(tick);\n }\n\n // ── Frame lifecycle ──\n\n /**\n * Apply the newest pending server snapshot (full-world rollback).\n * Call once per render frame BEFORE tickKeeper.update().\n * No-op on host or when nothing is pending.\n */\n beginFrame(): void {\n if (this._transport.isHost || !this._pending) return;\n\n const pending = this._pending;\n this._pending = null;\n this._lastAppliedServerTick = pending.t;\n\n // Consume the host's embedded input: last-known AND tick history at the snapshot tick\n if (pending.hostInput !== undefined) {\n this._inputs.setRemote(\n this._transport.hostId,\n pending.hostInput,\n pending.t,\n );\n }\n\n // Rollback is impossible without world access (bookkeeping above still happened)\n if (!this._worldDriver) return;\n\n const result = applyRollback({\n serverTick: pending.t,\n serverState: pending.entities,\n localTick: this._tickKeeper.tick,\n localPeerId: this._transport.peerId,\n inputs: this._inputs,\n currentErrors: this._errors,\n driver: this._worldDriver,\n callback: this._onPhysicsStep,\n dt: this._tickKeeper.tickDelta,\n driftTargetTicks: this._options.driftTargetTicks,\n maxRewindTicks: this._options.maxRewindTicks,\n snapThreshold: this._options.snapThreshold,\n });\n this._errors = result.errors;\n if (result.newLocalTick !== this._tickKeeper.tick) {\n this._tickKeeper.snapTick(result.newLocalTick);\n }\n }\n\n /**\n * Run one forward fixed tick (host AND client): store + broadcast input,\n * build per-tick input maps, invoke the callback, then step the world.\n */\n tick(tick: number): void {\n const localInput = this._currentInput ?? { ...this._options.neutralInput };\n\n // Store a copy so each tick gets its own history entry\n this._inputs.storeTick(tick, localInput);\n\n // Broadcast to ALL peers every tick (reliable-ordered guarantees tick history)\n this._inputChannel.send({\n t: tick,\n i: localInput,\n p: this._transport.peerId,\n });\n\n // Build per-tick maps: last-known remote inputs (hold-last-input extrapolation)\n const prevTick = tick - 1;\n const tickInputs = this._inputs.allRemotes();\n tickInputs.delete(this._transport.peerId); // defensive: never simulate self as remote\n const justPressed = new Map<string, PlayerInput>();\n for (const [peerId, inp] of tickInputs) {\n justPressed.set(\n peerId,\n computeJustPressed(inp, this._inputs.getRemoteAtTick(peerId, prevTick)),\n );\n }\n tickInputs.set(this._transport.peerId, localInput);\n justPressed.set(\n this._transport.peerId,\n this._inputs.hasTick(prevTick)\n ? computeJustPressed(localInput, this._inputs.getTick(prevTick))\n : this._inputs.getJustPressedZero(), // suppress spurious edges after snap/rejoin\n );\n\n if (this._onPhysicsStep) {\n this._onPhysicsStep(\n tickInputs,\n justPressed,\n tick,\n false,\n this._tickKeeper.tickDelta,\n );\n }\n\n // Callback first (applies forces), then step\n this._worldDriver?.stepWorld?.();\n }\n\n /**\n * Decay stored error offsets and return the portion to ADD to rendered\n * transforms this frame. Call exactly once per render frame.\n */\n getRenderErrorOffsets(): Map<string, ErrorOffset> {\n const result = new Map<string, ErrorOffset>();\n const decay = this._options.errorDecay;\n const maxErr = this._options.maxErrorPerFrame;\n\n for (const [id, e] of this._errors) {\n // Decay\n e.x *= decay;\n e.y *= decay;\n e.z *= decay;\n e.a *= decay;\n let q = quatScaleAngle({ x: e.qx, y: e.qy, z: e.qz, w: e.qw }, decay);\n\n // Zero-clamp\n if (Math.abs(e.x) < 0.1) e.x = 0;\n if (Math.abs(e.y) < 0.1) e.y = 0;\n if (Math.abs(e.z) < 0.1) e.z = 0;\n if (Math.abs(e.a) < 0.001) e.a = 0;\n if (quatAngle(q) < 0.001) q = { x: 0, y: 0, z: 0, w: 1 };\n e.qx = q.x;\n e.qy = q.y;\n e.qz = q.z;\n e.qw = q.w;\n\n // Applied portion (position cap only; angular error always applied in full)\n let ax = e.x;\n let ay = e.y;\n let az = e.z;\n if (maxErr > 0) {\n const mag = Math.hypot(e.x, e.y, e.z);\n if (mag > maxErr) {\n const s = maxErr / mag;\n ax = e.x * s;\n ay = e.y * s;\n az = e.z * s;\n e.x -= ax;\n e.y -= ay;\n e.z -= az;\n } else {\n // Fully applied and consumed\n e.x = 0;\n e.y = 0;\n e.z = 0;\n }\n }\n\n const quatIsIdentity =\n e.qx === 0 && e.qy === 0 && e.qz === 0 && e.qw === 1;\n\n if (ax !== 0 || ay !== 0 || az !== 0 || e.a !== 0 || !quatIsIdentity) {\n result.set(id, {\n x: ax,\n y: ay,\n z: az,\n a: e.a,\n qx: e.qx,\n qy: e.qy,\n qz: e.qz,\n qw: e.qw,\n });\n }\n\n if (e.x === 0 && e.y === 0 && e.z === 0 && e.a === 0 && quatIsIdentity) {\n this._errors.delete(id);\n }\n }\n\n return result;\n }\n\n // ── State ──\n\n /** Tick of the newest RECEIVED snapshot. */\n get serverTick(): number {\n return this._serverTick;\n }\n\n /** Tick of the newest APPLIED (rolled-back) snapshot, 0 initially. */\n get lastAppliedServerTick(): number {\n return this._lastAppliedServerTick;\n }\n\n destroy(): void {\n this._inputChannel.close();\n this._inputs.clear();\n this._errors.clear();\n this._pending = null;\n this._currentInput = null;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SignalingStrategy, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement, F as FirebaseStrategyConfig } from './types-
|
|
1
|
+
import { S as SignalingStrategy, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement, F as FirebaseStrategyConfig } from './types-hNfCIBzj.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* MQTT-based signaling strategy.
|
|
@@ -37,6 +37,8 @@ declare class MqttStrategy implements SignalingStrategy {
|
|
|
37
37
|
leaveRoom(): Promise<void>;
|
|
38
38
|
signal(targetPeerId: string, data: unknown): void;
|
|
39
39
|
subscribeToLobby(cb: (rooms: RoomAnnouncement[]) => void): () => void;
|
|
40
|
+
private _lastAnnouncement;
|
|
41
|
+
updateRoomOccupancy(roomId: string, playerCount: number, state?: 'lobby' | 'playing' | 'ended'): void;
|
|
40
42
|
announceRoom(announcement: RoomAnnouncement): void;
|
|
41
43
|
removeRoomAnnouncement(roomId: string): void;
|
|
42
44
|
onPeerDiscovered(cb: (peerId: string, meta: PeerMetadata) => void): () => void;
|
|
@@ -80,6 +82,8 @@ declare class FirebaseStrategy implements SignalingStrategy {
|
|
|
80
82
|
private _onLobby;
|
|
81
83
|
private _knownPeers;
|
|
82
84
|
private _lobbyAnnounceTimer;
|
|
85
|
+
private _lastAnnouncement;
|
|
86
|
+
private _lobbyWired;
|
|
83
87
|
private _destroyed;
|
|
84
88
|
constructor(appId: string, config: FirebaseStrategyConfig);
|
|
85
89
|
init(): Promise<void>;
|
|
@@ -88,6 +92,7 @@ declare class FirebaseStrategy implements SignalingStrategy {
|
|
|
88
92
|
signal(targetPeerId: string, data: unknown): void;
|
|
89
93
|
subscribeToLobby(cb: (rooms: RoomAnnouncement[]) => void): () => void;
|
|
90
94
|
announceRoom(announcement: RoomAnnouncement): void;
|
|
95
|
+
updateRoomOccupancy(roomId: string, playerCount: number, state?: 'lobby' | 'playing' | 'ended'): void;
|
|
91
96
|
removeRoomAnnouncement(roomId: string): void;
|
|
92
97
|
onPeerDiscovered(cb: (peerId: string, meta: PeerMetadata) => void): () => void;
|
|
93
98
|
onPeerLeft(cb: (peerId: string) => void): () => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as SignalingStrategy, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement, F as FirebaseStrategyConfig } from './types-
|
|
1
|
+
import { S as SignalingStrategy, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement, F as FirebaseStrategyConfig } from './types-hNfCIBzj.mjs';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* MQTT-based signaling strategy.
|
|
@@ -37,6 +37,8 @@ declare class MqttStrategy implements SignalingStrategy {
|
|
|
37
37
|
leaveRoom(): Promise<void>;
|
|
38
38
|
signal(targetPeerId: string, data: unknown): void;
|
|
39
39
|
subscribeToLobby(cb: (rooms: RoomAnnouncement[]) => void): () => void;
|
|
40
|
+
private _lastAnnouncement;
|
|
41
|
+
updateRoomOccupancy(roomId: string, playerCount: number, state?: 'lobby' | 'playing' | 'ended'): void;
|
|
40
42
|
announceRoom(announcement: RoomAnnouncement): void;
|
|
41
43
|
removeRoomAnnouncement(roomId: string): void;
|
|
42
44
|
onPeerDiscovered(cb: (peerId: string, meta: PeerMetadata) => void): () => void;
|
|
@@ -80,6 +82,8 @@ declare class FirebaseStrategy implements SignalingStrategy {
|
|
|
80
82
|
private _onLobby;
|
|
81
83
|
private _knownPeers;
|
|
82
84
|
private _lobbyAnnounceTimer;
|
|
85
|
+
private _lastAnnouncement;
|
|
86
|
+
private _lobbyWired;
|
|
83
87
|
private _destroyed;
|
|
84
88
|
constructor(appId: string, config: FirebaseStrategyConfig);
|
|
85
89
|
init(): Promise<void>;
|
|
@@ -88,6 +92,7 @@ declare class FirebaseStrategy implements SignalingStrategy {
|
|
|
88
92
|
signal(targetPeerId: string, data: unknown): void;
|
|
89
93
|
subscribeToLobby(cb: (rooms: RoomAnnouncement[]) => void): () => void;
|
|
90
94
|
announceRoom(announcement: RoomAnnouncement): void;
|
|
95
|
+
updateRoomOccupancy(roomId: string, playerCount: number, state?: 'lobby' | 'playing' | 'ended'): void;
|
|
91
96
|
removeRoomAnnouncement(roomId: string): void;
|
|
92
97
|
onPeerDiscovered(cb: (peerId: string, meta: PeerMetadata) => void): () => void;
|
|
93
98
|
onPeerLeft(cb: (peerId: string) => void): () => void;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { S as SignalingStrategy, a as StrategyConfig } from './types-
|
|
4
|
-
export { F as FirebaseStrategyConfig, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement } from './types-
|
|
5
|
-
import {
|
|
6
|
-
export {
|
|
7
|
-
export { F as FirebaseStrategy, M as MqttStrategy } from './firebase-
|
|
3
|
+
import { S as SignalingStrategy, a as StrategyConfig } from './types-hNfCIBzj.mjs';
|
|
4
|
+
export { F as FirebaseStrategyConfig, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement } from './types-hNfCIBzj.mjs';
|
|
5
|
+
import { r as NetworkManager, U as UseRoomOptions, s as ConnectionState, R as Room, t as CarverMultiplayerError, C as CarverTransport, u as UseLobbyOptions, v as RoomConfig, a as Player, d as RoomState, w as UseMultiplayerOptions, N as NetworkQuality, q as SyncMode, P as PlayerInput, E as EntityState } from './NetworkManager-D-DxFgdM.mjs';
|
|
6
|
+
export { c as CarverChannel, x as CarverErrorCode, b as ChannelOptions, m as EntityState2D, n as EntityState3D, l as ErrorOffset, o as EventPacket, I as InputPacket, J as JoinOptions, M as MultiplayerContextValue, j as PhysicsStepCallback, i as PredictionSyncOptions, k as PredictionWorldDriver, f as SnapshotListener, p as SnapshotPacket, g as SnapshotSource, T as TransportConfig } from './NetworkManager-D-DxFgdM.mjs';
|
|
7
|
+
export { F as FirebaseStrategy, M as MqttStrategy } from './firebase-GrbVrNgs.mjs';
|
|
8
|
+
export { I as InputBuffer } from './InputBuffer-J6XT_Tt0.mjs';
|
|
8
9
|
|
|
9
10
|
interface MultiplayerContextValue {
|
|
10
11
|
appId: string;
|
|
@@ -137,6 +138,8 @@ interface UseMultiplayerReturn {
|
|
|
137
138
|
serverTick: number;
|
|
138
139
|
drift: number;
|
|
139
140
|
syncEngine: SyncMode;
|
|
141
|
+
/** Set the local player's input (prediction mode). Stable callback; no-op in events/snapshot modes. */
|
|
142
|
+
setInput: (input: PlayerInput) => void;
|
|
140
143
|
}
|
|
141
144
|
declare function useMultiplayer(options?: UseMultiplayerOptions): UseMultiplayerReturn;
|
|
142
145
|
|
|
@@ -313,4 +316,22 @@ declare class InterestManager {
|
|
|
313
316
|
private _cellKey;
|
|
314
317
|
}
|
|
315
318
|
|
|
316
|
-
|
|
319
|
+
/**
|
|
320
|
+
* Input utilities for prediction mode.
|
|
321
|
+
* Ported from LumberNet's InputUtils.
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Given the input state at the current tick and the previous tick, return a new
|
|
326
|
+
* input object where each boolean field is `true` only if it transitioned
|
|
327
|
+
* false -> true (a "rising edge" / "just pressed").
|
|
328
|
+
*
|
|
329
|
+
* Non-boolean fields are passed through unchanged from `curr`.
|
|
330
|
+
* Iterates `curr` keys only; keys present only in `prev` are absent from the result.
|
|
331
|
+
*
|
|
332
|
+
* PredictionSync calls this automatically and passes the result as `justPressed`
|
|
333
|
+
* to every onPhysicsStep callback, so games normally do not call it directly.
|
|
334
|
+
*/
|
|
335
|
+
declare function computeJustPressed<I extends PlayerInput>(curr: I, prev: I): I;
|
|
336
|
+
|
|
337
|
+
export { CarverMultiplayerError, CarverTransport, ConnectionState, DebugOverlay, type DebugOverlayOptions, type DebugStats, EntityState, InterestManager, type InterestManagerOptions, MultiplayerBridge, MultiplayerProvider, type MultiplayerProviderProps, NetworkQuality, NetworkSimulator, type NetworkSimulatorOptions, Player, PlayerInput, Room, RoomConfig, RoomState, SignalingStrategy, StrategyConfig, SyncMode, UseLobbyOptions, UseMultiplayerOptions, UseRoomOptions, computeJustPressed, useHost, useLobby, useMultiplayer, useNetworkEvents, useNetworkState, usePlayers, useRoom };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { S as SignalingStrategy, a as StrategyConfig } from './types-
|
|
4
|
-
export { F as FirebaseStrategyConfig, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement } from './types-
|
|
5
|
-
import {
|
|
6
|
-
export {
|
|
7
|
-
export { F as FirebaseStrategy, M as MqttStrategy } from './firebase-
|
|
3
|
+
import { S as SignalingStrategy, a as StrategyConfig } from './types-hNfCIBzj.js';
|
|
4
|
+
export { F as FirebaseStrategyConfig, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement } from './types-hNfCIBzj.js';
|
|
5
|
+
import { r as NetworkManager, U as UseRoomOptions, s as ConnectionState, R as Room, t as CarverMultiplayerError, C as CarverTransport, u as UseLobbyOptions, v as RoomConfig, a as Player, d as RoomState, w as UseMultiplayerOptions, N as NetworkQuality, q as SyncMode, P as PlayerInput, E as EntityState } from './NetworkManager-DH9uGVMg.js';
|
|
6
|
+
export { c as CarverChannel, x as CarverErrorCode, b as ChannelOptions, m as EntityState2D, n as EntityState3D, l as ErrorOffset, o as EventPacket, I as InputPacket, J as JoinOptions, M as MultiplayerContextValue, j as PhysicsStepCallback, i as PredictionSyncOptions, k as PredictionWorldDriver, f as SnapshotListener, p as SnapshotPacket, g as SnapshotSource, T as TransportConfig } from './NetworkManager-DH9uGVMg.js';
|
|
7
|
+
export { F as FirebaseStrategy, M as MqttStrategy } from './firebase-B5MgLlHk.js';
|
|
8
|
+
export { I as InputBuffer } from './InputBuffer-V7XfHbc6.js';
|
|
8
9
|
|
|
9
10
|
interface MultiplayerContextValue {
|
|
10
11
|
appId: string;
|
|
@@ -137,6 +138,8 @@ interface UseMultiplayerReturn {
|
|
|
137
138
|
serverTick: number;
|
|
138
139
|
drift: number;
|
|
139
140
|
syncEngine: SyncMode;
|
|
141
|
+
/** Set the local player's input (prediction mode). Stable callback; no-op in events/snapshot modes. */
|
|
142
|
+
setInput: (input: PlayerInput) => void;
|
|
140
143
|
}
|
|
141
144
|
declare function useMultiplayer(options?: UseMultiplayerOptions): UseMultiplayerReturn;
|
|
142
145
|
|
|
@@ -313,4 +316,22 @@ declare class InterestManager {
|
|
|
313
316
|
private _cellKey;
|
|
314
317
|
}
|
|
315
318
|
|
|
316
|
-
|
|
319
|
+
/**
|
|
320
|
+
* Input utilities for prediction mode.
|
|
321
|
+
* Ported from LumberNet's InputUtils.
|
|
322
|
+
*/
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Given the input state at the current tick and the previous tick, return a new
|
|
326
|
+
* input object where each boolean field is `true` only if it transitioned
|
|
327
|
+
* false -> true (a "rising edge" / "just pressed").
|
|
328
|
+
*
|
|
329
|
+
* Non-boolean fields are passed through unchanged from `curr`.
|
|
330
|
+
* Iterates `curr` keys only; keys present only in `prev` are absent from the result.
|
|
331
|
+
*
|
|
332
|
+
* PredictionSync calls this automatically and passes the result as `justPressed`
|
|
333
|
+
* to every onPhysicsStep callback, so games normally do not call it directly.
|
|
334
|
+
*/
|
|
335
|
+
declare function computeJustPressed<I extends PlayerInput>(curr: I, prev: I): I;
|
|
336
|
+
|
|
337
|
+
export { CarverMultiplayerError, CarverTransport, ConnectionState, DebugOverlay, type DebugOverlayOptions, type DebugStats, EntityState, InterestManager, type InterestManagerOptions, MultiplayerBridge, MultiplayerProvider, type MultiplayerProviderProps, NetworkQuality, NetworkSimulator, type NetworkSimulatorOptions, Player, PlayerInput, Room, RoomConfig, RoomState, SignalingStrategy, StrategyConfig, SyncMode, UseLobbyOptions, UseMultiplayerOptions, UseRoomOptions, computeJustPressed, useHost, useLobby, useMultiplayer, useNetworkEvents, useNetworkState, usePlayers, useRoom };
|