@carverjs/multiplayer 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/NetworkManager-DrKM2tEx.d.mts +369 -0
  2. package/dist/NetworkManager-nvVAOr1O.d.ts +369 -0
  3. package/dist/chunk-3KT73N2S.mjs +655 -0
  4. package/dist/chunk-3KT73N2S.mjs.map +1 -0
  5. package/dist/chunk-EO3YNPRQ.mjs +817 -0
  6. package/dist/chunk-EO3YNPRQ.mjs.map +1 -0
  7. package/dist/chunk-UD6FDZMX.mjs +581 -0
  8. package/dist/chunk-UD6FDZMX.mjs.map +1 -0
  9. package/dist/firebase-CPu87KA0.d.ts +100 -0
  10. package/dist/firebase-PE6MxGdJ.d.mts +100 -0
  11. package/dist/index.d.mts +316 -0
  12. package/dist/index.d.ts +316 -0
  13. package/dist/index.js +3817 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/index.mjs +1743 -0
  16. package/dist/index.mjs.map +1 -0
  17. package/dist/strategy.d.mts +7 -0
  18. package/dist/strategy.d.ts +7 -0
  19. package/dist/strategy.js +619 -0
  20. package/dist/strategy.js.map +1 -0
  21. package/dist/strategy.mjs +11 -0
  22. package/dist/strategy.mjs.map +1 -0
  23. package/dist/sync.d.mts +212 -0
  24. package/dist/sync.d.ts +212 -0
  25. package/dist/sync.js +845 -0
  26. package/dist/sync.js.map +1 -0
  27. package/dist/sync.mjs +11 -0
  28. package/dist/sync.mjs.map +1 -0
  29. package/dist/transport.d.mts +159 -0
  30. package/dist/transport.d.ts +159 -0
  31. package/dist/transport.js +1274 -0
  32. package/dist/transport.js.map +1 -0
  33. package/dist/transport.mjs +19 -0
  34. package/dist/transport.mjs.map +1 -0
  35. package/dist/types-5LHBOW08.d.mts +74 -0
  36. package/dist/types-5LHBOW08.d.ts +74 -0
  37. package/dist/types.d.mts +2 -0
  38. package/dist/types.d.ts +2 -0
  39. package/dist/types.js +19 -0
  40. package/dist/types.js.map +1 -0
  41. package/dist/types.mjs +1 -0
  42. package/dist/types.mjs.map +1 -0
  43. package/package.json +73 -0
@@ -0,0 +1,316 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { S as SignalingStrategy, a as StrategyConfig } from './types-5LHBOW08.js';
4
+ export { F as FirebaseStrategyConfig, M as MqttStrategyConfig, P as PeerMetadata, R as RoomAnnouncement } from './types-5LHBOW08.js';
5
+ import { k as NetworkManager, U as UseRoomOptions, l as ConnectionState, R as Room, m as CarverMultiplayerError, C as CarverTransport, n as UseLobbyOptions, o as RoomConfig, P as Player, c as RoomState, p as UseMultiplayerOptions, N as NetworkQuality, j as SyncMode, E as EntityState } from './NetworkManager-nvVAOr1O.js';
6
+ export { b as CarverChannel, q as CarverErrorCode, a as ChannelOptions, f as EntityState2D, g as EntityState3D, h as EventPacket, I as InputPacket, J as JoinOptions, M as MultiplayerContextValue, i as SnapshotPacket, T as TransportConfig } from './NetworkManager-nvVAOr1O.js';
7
+ export { F as FirebaseStrategy, M as MqttStrategy } from './firebase-CPu87KA0.js';
8
+
9
+ interface MultiplayerContextValue {
10
+ appId: string;
11
+ strategy: SignalingStrategy;
12
+ iceServers?: RTCIceServer[];
13
+ networkManager: NetworkManager;
14
+ }
15
+
16
+ interface MultiplayerProviderProps {
17
+ /**
18
+ * Unique application identifier. Used to namespace rooms across the
19
+ * signaling network so different games don't interfere.
20
+ */
21
+ appId: string;
22
+ /**
23
+ * Signaling strategy configuration. Defaults to MQTT (free public brokers).
24
+ *
25
+ * ```tsx
26
+ * // Free (MQTT, zero config)
27
+ * <MultiplayerProvider appId="my-game">
28
+ *
29
+ * // Firebase (your own project)
30
+ * <MultiplayerProvider
31
+ * appId="my-game"
32
+ * strategy={{ type: 'firebase', databaseURL: 'https://my-project.firebaseio.com' }}
33
+ * >
34
+ * ```
35
+ */
36
+ strategy?: StrategyConfig;
37
+ /**
38
+ * ICE servers (STUN + TURN). Defaults to public Google/Cloudflare STUN.
39
+ *
40
+ * To add your own TURN server (e.g. Cloudflare TURN):
41
+ * ```tsx
42
+ * <MultiplayerProvider
43
+ * appId="my-game"
44
+ * iceServers={[
45
+ * { urls: 'stun:stun.cloudflare.com:3478' },
46
+ * { urls: 'turn:turn.cloudflare.com:3478', username: '...', credential: '...' },
47
+ * ]}
48
+ * >
49
+ * ```
50
+ */
51
+ iceServers?: RTCIceServer[];
52
+ children: ReactNode;
53
+ }
54
+ declare function MultiplayerProvider({ appId, strategy: strategyConfig, iceServers, children, }: MultiplayerProviderProps): react.FunctionComponentElement<react.ProviderProps<MultiplayerContextValue | null>>;
55
+
56
+ interface MultiplayerBridgeProps {
57
+ children: ReactNode;
58
+ }
59
+ /**
60
+ * Bridges the MultiplayerContext from the parent React tree into an R3F Canvas.
61
+ *
62
+ * R3F's `<Canvas>` uses a separate React reconciler, so React contexts from the
63
+ * parent tree are not automatically available inside the Canvas. This component
64
+ * reads the MultiplayerContext value from the parent tree and re-provides it
65
+ * inside the Canvas, making all CarverJS multiplayer hooks work seamlessly.
66
+ *
67
+ * Place `<MultiplayerBridge>` as a direct child of `<Game>`, wrapping `<World>`
68
+ * and all scene content that uses multiplayer hooks.
69
+ *
70
+ * @example
71
+ * ```tsx
72
+ * <MultiplayerProvider appId="my-game">
73
+ * <Game mode="2d">
74
+ * <MultiplayerBridge>
75
+ * <World>
76
+ * <MyScene />
77
+ * </World>
78
+ * </MultiplayerBridge>
79
+ * </Game>
80
+ * </MultiplayerProvider>
81
+ * ```
82
+ */
83
+ declare function MultiplayerBridge({ children }: MultiplayerBridgeProps): react.FunctionComponentElement<react.ProviderProps<MultiplayerContextValue | null>> | react.DOMElement<react.DOMAttributes<Element>, Element>;
84
+
85
+ interface UseRoomReturn {
86
+ roomId: string | null;
87
+ connectionState: ConnectionState;
88
+ isHost: boolean;
89
+ hostId: string | null;
90
+ selfId: string | null;
91
+ room: Room | null;
92
+ error: CarverMultiplayerError | null;
93
+ join: (roomId: string, options?: {
94
+ password?: string;
95
+ }) => Promise<void>;
96
+ leave: () => void;
97
+ setReady: (ready: boolean) => void;
98
+ setMetadata: (meta: Record<string, unknown>) => void;
99
+ setRoomMetadata: (meta: Record<string, unknown>) => void;
100
+ transport: CarverTransport | null;
101
+ }
102
+ declare function useRoom(roomId?: string, options?: UseRoomOptions): UseRoomReturn;
103
+
104
+ interface UseLobbyReturn {
105
+ rooms: Room[];
106
+ isLoading: boolean;
107
+ error: CarverMultiplayerError | null;
108
+ refresh: () => void;
109
+ createRoom: (config: RoomConfig) => Promise<string>;
110
+ }
111
+ declare function useLobby(options?: UseLobbyOptions): UseLobbyReturn;
112
+
113
+ interface UsePlayersReturn {
114
+ players: Player[];
115
+ self: Player | null;
116
+ host: Player | null;
117
+ count: number;
118
+ allReady: boolean;
119
+ getPlayer: (peerId: string) => Player | undefined;
120
+ }
121
+ declare function usePlayers(): UsePlayersReturn;
122
+
123
+ interface UseHostReturn {
124
+ kick: (peerId: string, reason?: string) => void;
125
+ transferHost: (peerId: string) => void;
126
+ setRoomState: (state: RoomState) => void;
127
+ setMaxPlayers: (n: number) => void;
128
+ lockRoom: () => void;
129
+ unlockRoom: () => void;
130
+ }
131
+ declare function useHost(): UseHostReturn;
132
+
133
+ interface UseMultiplayerReturn {
134
+ isActive: boolean;
135
+ networkQuality: NetworkQuality;
136
+ tick: number;
137
+ serverTick: number;
138
+ drift: number;
139
+ syncEngine: SyncMode;
140
+ }
141
+ declare function useMultiplayer(options?: UseMultiplayerOptions): UseMultiplayerReturn;
142
+
143
+ interface UseNetworkEventsReturn<T extends {
144
+ [K in keyof T]: unknown;
145
+ } = Record<string, unknown>> {
146
+ sendEvent: <K extends keyof T & string>(type: K, payload: T[K], target?: string) => void;
147
+ broadcast: <K extends keyof T & string>(type: K, payload: T[K]) => void;
148
+ onEvent: <K extends keyof T & string>(type: K, callback: (data: T[K], peerId: string) => void) => () => void;
149
+ }
150
+ declare function useNetworkEvents<T extends {
151
+ [K in keyof T]: unknown;
152
+ } = Record<string, unknown>>(options?: {
153
+ hostValidation?: boolean;
154
+ }): UseNetworkEventsReturn<T>;
155
+
156
+ interface UseNetworkStateReturn {
157
+ /** Host only. Create a networked entity and broadcast its existence to all peers. */
158
+ spawn: (id: string, initialState: EntityState) => void;
159
+ /** Host only. Remove a networked entity and broadcast removal to all peers. */
160
+ despawn: (id: string) => void;
161
+ /** Client sends a spawn request to the host, who validates and spawns if the id is not taken. */
162
+ requestSpawn: (id: string, config: Record<string, unknown>) => void;
163
+ /** Read the current state of a networked entity by id. */
164
+ getState: (id: string) => EntityState | undefined;
165
+ /** Write a partial update to a networked entity. Host-authoritative: only the host may call this. */
166
+ setState: (id: string, partialState: Partial<EntityState>) => void;
167
+ /** Reactive map of all current networked entity states. */
168
+ entities: Map<string, EntityState>;
169
+ }
170
+ /**
171
+ * Phase 4.4 – Advanced escape-hatch hook for direct networked entity management.
172
+ *
173
+ * Provides host-authoritative spawn / despawn, client requestSpawn,
174
+ * and direct state read / write over a reliable+ordered data channel.
175
+ */
176
+ declare function useNetworkState(): UseNetworkStateReturn;
177
+
178
+ interface DebugOverlayOptions {
179
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
180
+ /** Keyboard key used to toggle visibility (default: "F3") */
181
+ keyboardToggle?: string;
182
+ }
183
+ interface DebugStats {
184
+ tick: number;
185
+ serverTick: number;
186
+ drift: number;
187
+ /** Per-peer latency map or a single average value */
188
+ latencyMs: Map<string, number> | number;
189
+ /** Packet-loss rate in the range 0-1 */
190
+ packetLossRate: number;
191
+ /** Inbound bandwidth in bytes / second */
192
+ bandwidthIn: number;
193
+ /** Outbound bandwidth in bytes / second */
194
+ bandwidthOut: number;
195
+ networkQuality: "good" | "degraded" | "poor";
196
+ peerCount: number;
197
+ isHost: boolean;
198
+ syncMode: string;
199
+ }
200
+ declare class DebugOverlay {
201
+ private el;
202
+ private visible;
203
+ private lastHTML;
204
+ private readonly keyboardToggle;
205
+ private readonly handleKey;
206
+ constructor(options?: DebugOverlayOptions);
207
+ update(stats: DebugStats): void;
208
+ show(): void;
209
+ hide(): void;
210
+ toggle(): void;
211
+ destroy(): void;
212
+ private positionStyles;
213
+ private formatLatency;
214
+ }
215
+
216
+ interface NetworkSimulatorOptions {
217
+ /** Additional one-way latency in milliseconds (default 0) */
218
+ latencyMs?: number;
219
+ /** Packet drop rate, 0-1 (default 0) */
220
+ packetLoss?: number;
221
+ /** Random jitter +/- milliseconds (default 0) */
222
+ jitterMs?: number;
223
+ }
224
+ interface SimulatorStats {
225
+ sentCount: number;
226
+ droppedCount: number;
227
+ avgLatencyMs: number;
228
+ }
229
+ declare class NetworkSimulator {
230
+ private latencyMs;
231
+ private packetLoss;
232
+ private jitterMs;
233
+ private sentCount;
234
+ private droppedCount;
235
+ private latencySum;
236
+ /** Active timeout handles so we can cancel them on destroy() */
237
+ private pending;
238
+ constructor(options?: NetworkSimulatorOptions);
239
+ /** Update simulation parameters at runtime. */
240
+ setOptions(options: Partial<NetworkSimulatorOptions>): void;
241
+ /**
242
+ * Wrap an existing send function so that every call goes through the
243
+ * simulated network conditions (latency, jitter, packet loss).
244
+ */
245
+ wrapSend<T>(originalSend: (data: T, target?: string | string[]) => void): (data: T, target?: string | string[]) => void;
246
+ /** Current statistics snapshot. */
247
+ get stats(): SimulatorStats;
248
+ /** Cancel all pending delayed sends and clean up. */
249
+ destroy(): void;
250
+ }
251
+
252
+ interface InterestManagerOptions {
253
+ /** Spatial hash cell size in world units. Default: 50 */
254
+ cellSize?: number;
255
+ /** Default relevance radius around a client's position. Default: 200 */
256
+ defaultRadius?: number;
257
+ /** Entity ids/names that are always sent to every client. */
258
+ alwaysRelevant?: string[];
259
+ }
260
+ /**
261
+ * Spatial-hash-grid area-of-interest filter.
262
+ *
263
+ * Runs on the host side. Each tick the host feeds the full entity map via
264
+ * `updateEntities`, then queries per-client relevance with
265
+ * `getRelevantEntities` (or creates a single filter callback via
266
+ * `createFilter` for `HostAuthority.setInterestFilter`).
267
+ */
268
+ declare class InterestManager {
269
+ /** cell-key -> set of entity ids occupying that cell */
270
+ private _cells;
271
+ /** entity id -> last-known 3D position */
272
+ private _entityPositions;
273
+ private _cellSize;
274
+ private _defaultRadius;
275
+ private _alwaysRelevant;
276
+ constructor(options?: InterestManagerOptions);
277
+ /**
278
+ * Rebuild the spatial hash from the authoritative entity map.
279
+ * Called once per host tick before any relevance queries.
280
+ */
281
+ updateEntities(entities: Map<string, EntityState>): void;
282
+ /**
283
+ * Return the set of entity ids relevant to a single client.
284
+ *
285
+ * Relevance is the *union* of:
286
+ * 1. Entities whose cell overlaps the client's bounding sphere
287
+ * 2. Entities in the `alwaysRelevant` set
288
+ * 3. Entities owned by this client
289
+ */
290
+ getRelevantEntities(clientPosition: {
291
+ x: number;
292
+ y: number;
293
+ z?: number;
294
+ }, clientId: string, owners: Map<string, string>, overrideRadius?: number): Set<string>;
295
+ /**
296
+ * Build a filter callback compatible with
297
+ * `HostAuthority.setInterestFilter`.
298
+ *
299
+ * The returned function closes over a single relevance pass for every
300
+ * known client so that per-entity filtering during broadcast is a cheap
301
+ * `Set.has` lookup.
302
+ *
303
+ * @param clientPositions peerId -> position of that client's camera/player
304
+ * @param owners entityId -> ownerPeerId
305
+ */
306
+ createFilter(clientPositions: Map<string, {
307
+ x: number;
308
+ y: number;
309
+ z?: number;
310
+ }>, owners: Map<string, string>): (entityId: string, peerId: string) => boolean;
311
+ /** Remove all data from the grid. */
312
+ clear(): void;
313
+ private _cellKey;
314
+ }
315
+
316
+ export { CarverMultiplayerError, CarverTransport, ConnectionState, DebugOverlay, type DebugOverlayOptions, type DebugStats, EntityState, InterestManager, type InterestManagerOptions, MultiplayerBridge, MultiplayerProvider, type MultiplayerProviderProps, NetworkQuality, NetworkSimulator, type NetworkSimulatorOptions, Player, Room, RoomConfig, RoomState, SignalingStrategy, StrategyConfig, SyncMode, UseLobbyOptions, UseMultiplayerOptions, UseRoomOptions, useHost, useLobby, useMultiplayer, useNetworkEvents, useNetworkState, usePlayers, useRoom };