@smoregg/sdk 2.0.0 → 2.1.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/dist/cjs/controller.cjs +193 -115
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/errors.cjs +1 -0
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/events.cjs +19 -2
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/index.cjs +2 -7
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/screen.cjs +185 -130
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/shared.cjs +34 -0
- package/dist/cjs/shared.cjs.map +1 -0
- package/dist/cjs/testing.cjs +125 -74
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/cjs/transport/PostMessageTransport.cjs +12 -0
- package/dist/cjs/transport/PostMessageTransport.cjs.map +1 -1
- package/dist/cjs/transport/protocol.cjs +2 -0
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.cjs.map +1 -0
- package/dist/esm/controller.js +195 -117
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/errors.js +1 -0
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/events.js +18 -3
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/index.js +1 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/screen.js +187 -132
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/shared.js +30 -0
- package/dist/esm/shared.js.map +1 -0
- package/dist/esm/testing.js +125 -74
- package/dist/esm/testing.js.map +1 -1
- package/dist/esm/transport/PostMessageTransport.js +12 -0
- package/dist/esm/transport/PostMessageTransport.js.map +1 -1
- package/dist/esm/transport/protocol.js +2 -1
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/esm/types.js +14 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/types/controller.d.ts +1 -1
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/errors.d.ts.map +1 -1
- package/dist/types/events.d.ts +10 -1
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -8
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/screen.d.ts +3 -3
- package/dist/types/screen.d.ts.map +1 -1
- package/dist/types/shared.d.ts +21 -0
- package/dist/types/shared.d.ts.map +1 -0
- package/dist/types/testing.d.ts +63 -4
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/transport/PostMessageTransport.d.ts +1 -0
- package/dist/types/transport/PostMessageTransport.d.ts.map +1 -1
- package/dist/types/transport/protocol.d.ts +4 -0
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/types.d.ts +215 -347
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +442 -787
- package/dist/umd/smore-sdk.umd.js.map +1 -1
- package/dist/umd/smore-sdk.umd.min.js +1 -1
- package/dist/umd/smore-sdk.umd.min.js.map +1 -1
- package/package.json +7 -1
- package/dist/cjs/config.cjs +0 -13
- package/dist/cjs/config.cjs.map +0 -1
- package/dist/esm/config.js +0 -10
- package/dist/esm/config.js.map +0 -1
- package/dist/types/config.d.ts +0 -35
- package/dist/types/config.d.ts.map +0 -1
package/dist/types/types.d.ts
CHANGED
|
@@ -26,6 +26,11 @@ export type RoomCode = string;
|
|
|
26
26
|
* is enforced by the server's genericRelay handler. Payloads exceeding this
|
|
27
27
|
* limit will be silently dropped by the server without error notification.
|
|
28
28
|
*
|
|
29
|
+
* **Reserved Fields:** The field names `playerIndex` and `targetPlayerIndex` are reserved
|
|
30
|
+
* by the SDK for internal routing. Using these as custom data field names will cause
|
|
31
|
+
* a compile-time error. The SDK automatically extracts `playerIndex` to identify the sender
|
|
32
|
+
* and uses `targetPlayerIndex` for targeted message delivery.
|
|
33
|
+
*
|
|
29
34
|
* **Type Safety Note:** Without providing an explicit generic type parameter,
|
|
30
35
|
* `createScreen()` and `createController()` default to the empty `EventMap`,
|
|
31
36
|
* which means `send()`, `broadcast()`, and `on()` accept any string as an event
|
|
@@ -46,7 +51,7 @@ export type RoomCode = string;
|
|
|
46
51
|
*
|
|
47
52
|
* // With explicit generic -- full type safety
|
|
48
53
|
* const screen = createScreen<MyGameEvents>({ debug: true });
|
|
49
|
-
* screen.on('tap', (
|
|
54
|
+
* screen.on('tap', (playerIndex, data) => { ... });
|
|
50
55
|
* await screen.ready;
|
|
51
56
|
*
|
|
52
57
|
* // Without generic -- no type safety (not recommended)
|
|
@@ -64,7 +69,10 @@ export type RoomCode = string;
|
|
|
64
69
|
* ```
|
|
65
70
|
*/
|
|
66
71
|
export interface EventMap {
|
|
67
|
-
[key: string]: unknown
|
|
72
|
+
[key: string]: Record<string, unknown> & {
|
|
73
|
+
playerIndex?: never;
|
|
74
|
+
targetPlayerIndex?: never;
|
|
75
|
+
};
|
|
68
76
|
}
|
|
69
77
|
/**
|
|
70
78
|
* Extract event names from an event map.
|
|
@@ -93,7 +101,7 @@ export interface CharacterAppearance {
|
|
|
93
101
|
/** Character style preset identifier */
|
|
94
102
|
style: string;
|
|
95
103
|
/** Additional character customization options */
|
|
96
|
-
options: Record<string,
|
|
104
|
+
options: Record<string, unknown>;
|
|
97
105
|
}
|
|
98
106
|
/**
|
|
99
107
|
* Information about a connected controller (player).
|
|
@@ -124,6 +132,99 @@ export interface ControllerInfo {
|
|
|
124
132
|
*/
|
|
125
133
|
readonly appearance?: CharacterAppearance | null;
|
|
126
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Transport interface for custom communication implementations.
|
|
137
|
+
* Implement this to use a custom transport instead of the default PostMessageTransport.
|
|
138
|
+
*/
|
|
139
|
+
export interface Transport {
|
|
140
|
+
emit(event: string, ...args: unknown[]): void;
|
|
141
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
142
|
+
off(event: string, handler?: (...args: unknown[]) => void): void;
|
|
143
|
+
destroy(): void;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Error codes for SDK errors.
|
|
147
|
+
*/
|
|
148
|
+
export type SmoreErrorCode = 'TIMEOUT' | 'NOT_READY' | 'DESTROYED' | 'INVALID_EVENT' | 'INVALID_PLAYER' | 'CONNECTION_LOST' | 'INIT_FAILED' | 'RATE_LIMITED' | 'PAYLOAD_TOO_LARGE' | 'UNKNOWN';
|
|
149
|
+
/**
|
|
150
|
+
* Structured error type for SDK errors.
|
|
151
|
+
*/
|
|
152
|
+
export interface SmoreError {
|
|
153
|
+
/** Error code for programmatic handling */
|
|
154
|
+
code: SmoreErrorCode;
|
|
155
|
+
/** Human-readable error message */
|
|
156
|
+
message: string;
|
|
157
|
+
/** Original error if available */
|
|
158
|
+
cause?: Error;
|
|
159
|
+
/** Additional context */
|
|
160
|
+
details?: Record<string, unknown>;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Game results structure for gameOver().
|
|
164
|
+
* Flexible to accommodate different game types.
|
|
165
|
+
*/
|
|
166
|
+
export interface GameResults {
|
|
167
|
+
/** Player scores indexed by player index */
|
|
168
|
+
scores?: Record<PlayerIndex, number>;
|
|
169
|
+
/** Winner's player index (or -1 for no winner/tie) */
|
|
170
|
+
winner?: PlayerIndex;
|
|
171
|
+
/** Ranked player indices from first to last */
|
|
172
|
+
rankings?: PlayerIndex[];
|
|
173
|
+
/** Additional custom game-specific data */
|
|
174
|
+
custom?: Record<string, unknown>;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Lifecycle event names for subscribing via `on()`.
|
|
178
|
+
*
|
|
179
|
+
* These `$`-prefixed event names allow lifecycle events to be registered
|
|
180
|
+
* through the same `on()` method used for game events. The `$` prefix is
|
|
181
|
+
* reserved by the SDK and cannot be used for user-defined events.
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* ```ts
|
|
185
|
+
* // These are equivalent:
|
|
186
|
+
* screen.onControllerJoin((pi, info) => { ... });
|
|
187
|
+
* screen.on(LifecycleEvent.CONTROLLER_JOIN, (pi, info) => { ... });
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
export declare const LifecycleEvent: {
|
|
191
|
+
readonly ALL_READY: "$all-ready";
|
|
192
|
+
readonly CONTROLLER_JOIN: "$controller-join";
|
|
193
|
+
readonly CONTROLLER_LEAVE: "$controller-leave";
|
|
194
|
+
readonly CONTROLLER_DISCONNECT: "$controller-disconnect";
|
|
195
|
+
readonly CONTROLLER_RECONNECT: "$controller-reconnect";
|
|
196
|
+
readonly CHARACTER_UPDATED: "$character-updated";
|
|
197
|
+
readonly ERROR: "$error";
|
|
198
|
+
readonly GAME_OVER: "$game-over";
|
|
199
|
+
readonly CONNECTION_CHANGE: "$connection-change";
|
|
200
|
+
};
|
|
201
|
+
/** Union of all lifecycle event name strings. */
|
|
202
|
+
export type LifecycleEventName = typeof LifecycleEvent[keyof typeof LifecycleEvent];
|
|
203
|
+
/**
|
|
204
|
+
* Handler signatures for Screen lifecycle events.
|
|
205
|
+
* Used by `on()` overloads to provide type-safe lifecycle event subscription.
|
|
206
|
+
*/
|
|
207
|
+
export interface ScreenLifecycleHandlers {
|
|
208
|
+
'$all-ready': () => void;
|
|
209
|
+
'$controller-join': (playerIndex: PlayerIndex, info: ControllerInfo) => void;
|
|
210
|
+
'$controller-leave': (playerIndex: PlayerIndex) => void;
|
|
211
|
+
'$controller-disconnect': (playerIndex: PlayerIndex) => void;
|
|
212
|
+
'$controller-reconnect': (playerIndex: PlayerIndex, info: ControllerInfo) => void;
|
|
213
|
+
'$character-updated': (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void;
|
|
214
|
+
'$error': (error: SmoreError) => void;
|
|
215
|
+
'$connection-change': (connected: boolean) => void;
|
|
216
|
+
}
|
|
217
|
+
/** Screen lifecycle event name union. */
|
|
218
|
+
export type ScreenLifecycleEvent = keyof ScreenLifecycleHandlers;
|
|
219
|
+
/**
|
|
220
|
+
* Handler signatures for Controller lifecycle events.
|
|
221
|
+
* Extends Screen lifecycle events with Controller-specific events.
|
|
222
|
+
*/
|
|
223
|
+
export interface ControllerLifecycleHandlers extends ScreenLifecycleHandlers {
|
|
224
|
+
'$game-over': (results?: GameResults) => void;
|
|
225
|
+
}
|
|
226
|
+
/** Controller lifecycle event name union. */
|
|
227
|
+
export type ControllerLifecycleEvent = keyof ControllerLifecycleHandlers;
|
|
127
228
|
/**
|
|
128
229
|
* Handler for events received from controllers.
|
|
129
230
|
* Receives the player index and event data.
|
|
@@ -144,12 +245,12 @@ export type ScreenEventHandler<TData = unknown> = (playerIndex: PlayerIndex, dat
|
|
|
144
245
|
*
|
|
145
246
|
* screen.on('tap', (playerIndex, data) => handleTap(playerIndex, data));
|
|
146
247
|
* screen.onAllReady(() => startCountdown());
|
|
147
|
-
* screen.onControllerJoin((
|
|
248
|
+
* screen.onControllerJoin((playerIndex, info) => console.log('Joined:', playerIndex));
|
|
148
249
|
*
|
|
149
250
|
* await screen.ready;
|
|
150
251
|
* ```
|
|
151
252
|
*/
|
|
152
|
-
export interface ScreenConfig
|
|
253
|
+
export interface ScreenConfig {
|
|
153
254
|
/**
|
|
154
255
|
* Enable debug mode for verbose logging.
|
|
155
256
|
* @default false
|
|
@@ -166,6 +267,19 @@ export interface ScreenConfig<_TEvents extends EventMap = EventMap> {
|
|
|
166
267
|
* @default 10000
|
|
167
268
|
*/
|
|
168
269
|
timeout?: number;
|
|
270
|
+
/**
|
|
271
|
+
* Automatically signal ready after initialization.
|
|
272
|
+
* When true (default), the SDK calls signalReady() automatically after init completes.
|
|
273
|
+
* Set to false if your game needs to load resources before signaling ready.
|
|
274
|
+
* @default true
|
|
275
|
+
*/
|
|
276
|
+
autoReady?: boolean;
|
|
277
|
+
/**
|
|
278
|
+
* Custom transport implementation.
|
|
279
|
+
* If provided, uses this instead of the default PostMessageTransport.
|
|
280
|
+
* The bridge handshake (_bridge:init) still occurs via postMessage.
|
|
281
|
+
*/
|
|
282
|
+
transport?: Transport;
|
|
169
283
|
}
|
|
170
284
|
/**
|
|
171
285
|
* Screen instance - the main interface for the host/TV side of your game.
|
|
@@ -179,9 +293,9 @@ export interface ScreenConfig<_TEvents extends EventMap = EventMap> {
|
|
|
179
293
|
* ```ts
|
|
180
294
|
* const screen = createScreen<MyEvents>({ debug: true });
|
|
181
295
|
*
|
|
182
|
-
* screen.on('tap', (
|
|
296
|
+
* screen.on('tap', (playerIndex, data) => handleTap(playerIndex, data));
|
|
183
297
|
* screen.onAllReady(() => startGame());
|
|
184
|
-
* screen.onControllerJoin((
|
|
298
|
+
* screen.onControllerJoin((playerIndex, info) => addPlayer(playerIndex, info));
|
|
185
299
|
*
|
|
186
300
|
* await screen.ready;
|
|
187
301
|
* screen.broadcast('phase-update', { phase: 'playing' });
|
|
@@ -203,6 +317,10 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
203
317
|
readonly isReady: boolean;
|
|
204
318
|
/** Whether the screen has been destroyed. */
|
|
205
319
|
readonly isDestroyed: boolean;
|
|
320
|
+
/** Whether the connection to the server is active. */
|
|
321
|
+
readonly isConnected: boolean;
|
|
322
|
+
/** Protocol version negotiated with the parent frame. */
|
|
323
|
+
readonly protocolVersion: number;
|
|
206
324
|
/**
|
|
207
325
|
* A Promise that resolves when the screen is initialized and ready.
|
|
208
326
|
* Use this to await readiness after creating a screen synchronously.
|
|
@@ -259,13 +377,6 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
259
377
|
* @returns Unsubscribe function to remove the callback
|
|
260
378
|
*/
|
|
261
379
|
onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void;
|
|
262
|
-
/**
|
|
263
|
-
* Register a callback for when the server rate-limits an event.
|
|
264
|
-
*
|
|
265
|
-
* @param callback - Called with the rate-limited event name
|
|
266
|
-
* @returns Unsubscribe function to remove the callback
|
|
267
|
-
*/
|
|
268
|
-
onRateLimited(callback: (event: string) => void): () => void;
|
|
269
380
|
/**
|
|
270
381
|
* Register a callback for when an error occurs.
|
|
271
382
|
* If no error callback is registered, errors are logged to console in debug mode.
|
|
@@ -274,6 +385,14 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
274
385
|
* @returns Unsubscribe function to remove the callback
|
|
275
386
|
*/
|
|
276
387
|
onError(callback: (error: SmoreError) => void): () => void;
|
|
388
|
+
/**
|
|
389
|
+
* Register a callback for when the connection status changes.
|
|
390
|
+
* Called when the connection to the server is lost or restored.
|
|
391
|
+
*
|
|
392
|
+
* @param callback - Called with true (connected) or false (disconnected)
|
|
393
|
+
* @returns Unsubscribe function to remove the callback
|
|
394
|
+
*/
|
|
395
|
+
onConnectionChange(callback: (connected: boolean) => void): () => void;
|
|
277
396
|
/**
|
|
278
397
|
* Broadcast an event to all connected controllers.
|
|
279
398
|
*
|
|
@@ -288,11 +407,6 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
288
407
|
* ```
|
|
289
408
|
*/
|
|
290
409
|
broadcast<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void;
|
|
291
|
-
/**
|
|
292
|
-
* Broadcast an event with any data (bypasses type checking).
|
|
293
|
-
* Use sparingly - prefer the typed broadcast() method.
|
|
294
|
-
*/
|
|
295
|
-
broadcastRaw(event: string, data?: unknown): void;
|
|
296
410
|
/**
|
|
297
411
|
* Send an event to a specific controller.
|
|
298
412
|
*
|
|
@@ -310,11 +424,6 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
310
424
|
* ```
|
|
311
425
|
*/
|
|
312
426
|
sendToController<K extends EventNames<TEvents>>(playerIndex: PlayerIndex, event: K, data: EventData<TEvents, K>): void;
|
|
313
|
-
/**
|
|
314
|
-
* Send to controller with any data (bypasses type checking).
|
|
315
|
-
* Use sparingly - prefer the typed sendToController() method.
|
|
316
|
-
*/
|
|
317
|
-
sendToControllerRaw(playerIndex: PlayerIndex, event: string, data?: unknown): void;
|
|
318
427
|
/**
|
|
319
428
|
* Signal that the game is over and send results.
|
|
320
429
|
* This will broadcast a game-over event to all controllers.
|
|
@@ -345,6 +454,17 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
345
454
|
* ```
|
|
346
455
|
*/
|
|
347
456
|
signalReady(): void;
|
|
457
|
+
/**
|
|
458
|
+
* Subscribe to a lifecycle event or a user-defined game event.
|
|
459
|
+
*
|
|
460
|
+
* Lifecycle events use `$`-prefixed names. Use `LifecycleEvent` constants
|
|
461
|
+
* for type-safe subscription:
|
|
462
|
+
* ```ts
|
|
463
|
+
* screen.on(LifecycleEvent.CONTROLLER_JOIN, (pi, info) => { ... });
|
|
464
|
+
* screen.on('tap', (pi, data) => { ... }); // user event
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
on<K extends ScreenLifecycleEvent>(event: K, handler: ScreenLifecycleHandlers[K]): () => void;
|
|
348
468
|
/**
|
|
349
469
|
* Add a listener for a specific event after construction.
|
|
350
470
|
* Can be called before the screen is ready -- handlers are queued
|
|
@@ -365,6 +485,7 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
365
485
|
* ```
|
|
366
486
|
*/
|
|
367
487
|
on<K extends EventNames<TEvents>>(event: K, handler: ScreenEventHandler<EventData<TEvents, K>>): () => void;
|
|
488
|
+
once<K extends ScreenLifecycleEvent>(event: K, handler: ScreenLifecycleHandlers[K]): () => void;
|
|
368
489
|
/**
|
|
369
490
|
* Add a one-time listener that auto-removes after first call.
|
|
370
491
|
*
|
|
@@ -372,10 +493,19 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
372
493
|
* `off(event, originalHandler)`. Use the returned unsubscribe function instead.
|
|
373
494
|
*/
|
|
374
495
|
once<K extends EventNames<TEvents>>(event: K, handler: ScreenEventHandler<EventData<TEvents, K>>): () => void;
|
|
496
|
+
off<K extends ScreenLifecycleEvent>(event: K, handler?: ScreenLifecycleHandlers[K]): void;
|
|
375
497
|
/**
|
|
376
498
|
* Remove a specific listener or all listeners for an event.
|
|
377
499
|
*/
|
|
378
500
|
off<K extends EventNames<TEvents>>(event: K, handler?: ScreenEventHandler<EventData<TEvents, K>>): void;
|
|
501
|
+
/**
|
|
502
|
+
* Remove all event listeners, or all listeners for a specific event.
|
|
503
|
+
* Only removes user event listeners registered via on()/once().
|
|
504
|
+
* Lifecycle callbacks (onControllerJoin, onAllReady, etc.) are not affected.
|
|
505
|
+
*
|
|
506
|
+
* @param event - Optional event name. If omitted, removes ALL user event listeners.
|
|
507
|
+
*/
|
|
508
|
+
removeAllListeners(event?: string): void;
|
|
379
509
|
/**
|
|
380
510
|
* Get a specific controller by player index.
|
|
381
511
|
* Returns undefined if not found.
|
|
@@ -385,14 +515,6 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
385
515
|
* Get the number of connected controllers.
|
|
386
516
|
*/
|
|
387
517
|
getControllerCount(): number;
|
|
388
|
-
/**
|
|
389
|
-
* Check if there is at least one connected controller.
|
|
390
|
-
* Useful for detecting when all players have disconnected
|
|
391
|
-
* (e.g., to pause the game or show a waiting screen).
|
|
392
|
-
*
|
|
393
|
-
* Screen-only method. Controller instances can check their own connection status directly.
|
|
394
|
-
*/
|
|
395
|
-
hasAnyConnectedControllers(): boolean;
|
|
396
518
|
/**
|
|
397
519
|
* Clean up all resources and disconnect.
|
|
398
520
|
* Call this when unmounting/destroying your game.
|
|
@@ -424,7 +546,7 @@ export type ControllerEventHandler<TData = unknown> = (data: TData) => void;
|
|
|
424
546
|
* controller.send('tap', { x: 100, y: 200 });
|
|
425
547
|
* ```
|
|
426
548
|
*/
|
|
427
|
-
export interface ControllerConfig
|
|
549
|
+
export interface ControllerConfig {
|
|
428
550
|
/**
|
|
429
551
|
* Enable debug mode for verbose logging.
|
|
430
552
|
* @default false
|
|
@@ -440,6 +562,19 @@ export interface ControllerConfig<_TEvents extends EventMap = EventMap> {
|
|
|
440
562
|
* @default 10000
|
|
441
563
|
*/
|
|
442
564
|
timeout?: number;
|
|
565
|
+
/**
|
|
566
|
+
* Automatically signal ready after initialization.
|
|
567
|
+
* When true (default), the SDK calls signalReady() automatically after init completes.
|
|
568
|
+
* Set to false if your game needs to load resources before signaling ready.
|
|
569
|
+
* @default true
|
|
570
|
+
*/
|
|
571
|
+
autoReady?: boolean;
|
|
572
|
+
/**
|
|
573
|
+
* Custom transport implementation.
|
|
574
|
+
* If provided, uses this instead of the default PostMessageTransport.
|
|
575
|
+
* The bridge handshake (_bridge:init) still occurs via postMessage.
|
|
576
|
+
*/
|
|
577
|
+
transport?: Transport;
|
|
443
578
|
}
|
|
444
579
|
/**
|
|
445
580
|
* Controller instance - the main interface for the player/phone side.
|
|
@@ -470,13 +605,17 @@ export interface ControllerConfig<_TEvents extends EventMap = EventMap> {
|
|
|
470
605
|
*/
|
|
471
606
|
export interface Controller<TEvents extends EventMap = EventMap> {
|
|
472
607
|
/** My player index (0, 1, 2, ...). */
|
|
473
|
-
readonly
|
|
608
|
+
readonly myPlayerIndex: PlayerIndex;
|
|
474
609
|
/** The room code for this game session. */
|
|
475
610
|
readonly roomCode: RoomCode;
|
|
476
611
|
/** Whether the controller is initialized and ready. */
|
|
477
612
|
readonly isReady: boolean;
|
|
478
613
|
/** Whether the controller has been destroyed. */
|
|
479
614
|
readonly isDestroyed: boolean;
|
|
615
|
+
/** Whether the connection to the server is active. */
|
|
616
|
+
readonly isConnected: boolean;
|
|
617
|
+
/** Protocol version negotiated with the parent frame. */
|
|
618
|
+
readonly protocolVersion: number;
|
|
480
619
|
/**
|
|
481
620
|
* Read-only list of all known controllers (players) in the room.
|
|
482
621
|
* Returns full ControllerInfo including playerIndex, nickname, connected status, and appearance.
|
|
@@ -542,13 +681,6 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
542
681
|
* @returns Unsubscribe function to remove the callback
|
|
543
682
|
*/
|
|
544
683
|
onCharacterUpdated(callback: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void): () => void;
|
|
545
|
-
/**
|
|
546
|
-
* Register a callback for when the server rate-limits an event.
|
|
547
|
-
*
|
|
548
|
-
* @param callback - Called with the rate-limited event name
|
|
549
|
-
* @returns Unsubscribe function to remove the callback
|
|
550
|
-
*/
|
|
551
|
-
onRateLimited(callback: (event: string) => void): () => void;
|
|
552
684
|
/**
|
|
553
685
|
* Register a callback for when an error occurs.
|
|
554
686
|
* If no error callback is registered, errors are logged to console in debug mode.
|
|
@@ -557,10 +689,31 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
557
689
|
* @returns Unsubscribe function to remove the callback
|
|
558
690
|
*/
|
|
559
691
|
onError(callback: (error: SmoreError) => void): () => void;
|
|
692
|
+
/**
|
|
693
|
+
* Register a callback for when the game ends.
|
|
694
|
+
* Called when the Screen calls gameOver().
|
|
695
|
+
*
|
|
696
|
+
* @param callback - Called with optional game results
|
|
697
|
+
* @returns Unsubscribe function to remove the callback
|
|
698
|
+
*/
|
|
699
|
+
onGameOver(callback: (results?: GameResults) => void): () => void;
|
|
700
|
+
/**
|
|
701
|
+
* Register a callback for when the connection status changes.
|
|
702
|
+
* Called when the connection to the server is lost or restored.
|
|
703
|
+
*
|
|
704
|
+
* @param callback - Called with true (connected) or false (disconnected)
|
|
705
|
+
* @returns Unsubscribe function to remove the callback
|
|
706
|
+
*/
|
|
707
|
+
onConnectionChange(callback: (connected: boolean) => void): () => void;
|
|
560
708
|
/**
|
|
561
709
|
* Returns the number of currently connected players.
|
|
562
710
|
*/
|
|
563
711
|
getControllerCount(): number;
|
|
712
|
+
/**
|
|
713
|
+
* Get a specific controller by player index.
|
|
714
|
+
* Returns undefined if not found.
|
|
715
|
+
*/
|
|
716
|
+
getController(playerIndex: PlayerIndex): ControllerInfo | undefined;
|
|
564
717
|
/**
|
|
565
718
|
* Send an event to the screen.
|
|
566
719
|
*
|
|
@@ -577,11 +730,6 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
577
730
|
* ```
|
|
578
731
|
*/
|
|
579
732
|
send<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void;
|
|
580
|
-
/**
|
|
581
|
-
* Send with any data (bypasses type checking).
|
|
582
|
-
* Use sparingly - prefer the typed send() method.
|
|
583
|
-
*/
|
|
584
|
-
sendRaw(event: string, data?: unknown): void;
|
|
585
733
|
/**
|
|
586
734
|
* Signal that this controller has finished loading resources and is ready to start.
|
|
587
735
|
*
|
|
@@ -596,6 +744,17 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
596
744
|
* ```
|
|
597
745
|
*/
|
|
598
746
|
signalReady(): void;
|
|
747
|
+
/**
|
|
748
|
+
* Subscribe to a lifecycle event or a user-defined game event.
|
|
749
|
+
*
|
|
750
|
+
* Lifecycle events use `$`-prefixed names. Use `LifecycleEvent` constants
|
|
751
|
+
* for type-safe subscription:
|
|
752
|
+
* ```ts
|
|
753
|
+
* controller.on(LifecycleEvent.CONTROLLER_JOIN, (pi, info) => { ... });
|
|
754
|
+
* controller.on('phase-update', (data) => { ... }); // user event
|
|
755
|
+
* ```
|
|
756
|
+
*/
|
|
757
|
+
on<K extends ControllerLifecycleEvent>(event: K, handler: ControllerLifecycleHandlers[K]): () => void;
|
|
599
758
|
/**
|
|
600
759
|
* Add a listener for a specific event after construction.
|
|
601
760
|
* Can be called before the controller is ready -- handlers are queued
|
|
@@ -621,6 +780,7 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
621
780
|
* ```
|
|
622
781
|
*/
|
|
623
782
|
on<K extends EventNames<TEvents>>(event: K, handler: ControllerEventHandler<EventData<TEvents, K>>): () => void;
|
|
783
|
+
once<K extends ControllerLifecycleEvent>(event: K, handler: ControllerLifecycleHandlers[K]): () => void;
|
|
624
784
|
/**
|
|
625
785
|
* Add a one-time listener that auto-removes after first call.
|
|
626
786
|
*
|
|
@@ -628,47 +788,25 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
628
788
|
* `off(event, originalHandler)`. Use the returned unsubscribe function instead.
|
|
629
789
|
*/
|
|
630
790
|
once<K extends EventNames<TEvents>>(event: K, handler: ControllerEventHandler<EventData<TEvents, K>>): () => void;
|
|
791
|
+
off<K extends ControllerLifecycleEvent>(event: K, handler?: ControllerLifecycleHandlers[K]): void;
|
|
631
792
|
/**
|
|
632
793
|
* Remove a specific listener or all listeners for an event.
|
|
633
794
|
*/
|
|
634
795
|
off<K extends EventNames<TEvents>>(event: K, handler?: ControllerEventHandler<EventData<TEvents, K>>): void;
|
|
796
|
+
/**
|
|
797
|
+
* Remove all event listeners, or all listeners for a specific event.
|
|
798
|
+
* Only removes user event listeners registered via on()/once().
|
|
799
|
+
* Lifecycle callbacks (onControllerJoin, onAllReady, etc.) are not affected.
|
|
800
|
+
*
|
|
801
|
+
* @param event - Optional event name. If omitted, removes ALL user event listeners.
|
|
802
|
+
*/
|
|
803
|
+
removeAllListeners(event?: string): void;
|
|
635
804
|
/**
|
|
636
805
|
* Clean up all resources and disconnect.
|
|
637
806
|
* Call this when unmounting/destroying your game.
|
|
638
807
|
*/
|
|
639
808
|
destroy(): void;
|
|
640
809
|
}
|
|
641
|
-
/**
|
|
642
|
-
* Game results structure for gameOver().
|
|
643
|
-
* Flexible to accommodate different game types.
|
|
644
|
-
*/
|
|
645
|
-
export interface GameResults {
|
|
646
|
-
/** Player scores indexed by player index */
|
|
647
|
-
scores?: Record<PlayerIndex, number>;
|
|
648
|
-
/** Winner's player index (or -1 for no winner/tie) */
|
|
649
|
-
winner?: PlayerIndex;
|
|
650
|
-
/** Ranked player indices from first to last */
|
|
651
|
-
rankings?: PlayerIndex[];
|
|
652
|
-
/** Additional custom data */
|
|
653
|
-
[key: string]: unknown;
|
|
654
|
-
}
|
|
655
|
-
/**
|
|
656
|
-
* Error codes for SDK errors.
|
|
657
|
-
*/
|
|
658
|
-
export type SmoreErrorCode = 'TIMEOUT' | 'NOT_READY' | 'DESTROYED' | 'INVALID_EVENT' | 'INVALID_PLAYER' | 'CONNECTION_LOST' | 'INIT_FAILED' | 'UNKNOWN';
|
|
659
|
-
/**
|
|
660
|
-
* Structured error type for SDK errors.
|
|
661
|
-
*/
|
|
662
|
-
export interface SmoreError {
|
|
663
|
-
/** Error code for programmatic handling */
|
|
664
|
-
code: SmoreErrorCode;
|
|
665
|
-
/** Human-readable error message */
|
|
666
|
-
message: string;
|
|
667
|
-
/** Original error if available */
|
|
668
|
-
cause?: Error;
|
|
669
|
-
/** Additional context */
|
|
670
|
-
details?: Record<string, unknown>;
|
|
671
|
-
}
|
|
672
810
|
/**
|
|
673
811
|
* Log levels for debug output.
|
|
674
812
|
*/
|
|
@@ -692,274 +830,4 @@ export interface DebugOptions {
|
|
|
692
830
|
/** Custom logger function */
|
|
693
831
|
logger?: (level: LogLevel, message: string, data?: unknown) => void;
|
|
694
832
|
}
|
|
695
|
-
/**
|
|
696
|
-
* Create a Screen instance for the host/TV side of your game.
|
|
697
|
-
*
|
|
698
|
-
* Returns a Screen instance synchronously. The screen begins listening
|
|
699
|
-
* for the bridge init message immediately. Use the `.ready` promise
|
|
700
|
-
* to await full initialization.
|
|
701
|
-
*
|
|
702
|
-
* @template TEvents - Event map type for type-safe events
|
|
703
|
-
* @param config - Screen configuration (debug, parentOrigin, timeout)
|
|
704
|
-
* @returns Screen instance (synchronous)
|
|
705
|
-
*
|
|
706
|
-
* @example
|
|
707
|
-
* ```ts
|
|
708
|
-
* const screen = createScreen<MyEvents>({ debug: true });
|
|
709
|
-
*
|
|
710
|
-
* screen.on('tap', (playerIndex, data) => {
|
|
711
|
-
* screen.broadcast('round-result', { ... });
|
|
712
|
-
* });
|
|
713
|
-
*
|
|
714
|
-
* screen.onAllReady(() => startGame());
|
|
715
|
-
* screen.onControllerJoin((pi, info) => addPlayer(pi, info));
|
|
716
|
-
*
|
|
717
|
-
* await screen.ready;
|
|
718
|
-
* ```
|
|
719
|
-
*/
|
|
720
|
-
export declare function createScreen<TEvents extends EventMap = EventMap>(config?: ScreenConfig<TEvents>): Screen<TEvents>;
|
|
721
|
-
/**
|
|
722
|
-
* Create a Controller instance for the player/phone side of your game.
|
|
723
|
-
*
|
|
724
|
-
* Returns a Controller instance synchronously. The controller begins listening
|
|
725
|
-
* for the bridge init message immediately. Use the `.ready` promise
|
|
726
|
-
* to await full initialization.
|
|
727
|
-
*
|
|
728
|
-
* @template TEvents - Event map type for type-safe events
|
|
729
|
-
* @param config - Controller configuration (debug, parentOrigin, timeout)
|
|
730
|
-
* @returns Controller instance (synchronous)
|
|
731
|
-
*
|
|
732
|
-
* @example
|
|
733
|
-
* ```ts
|
|
734
|
-
* const controller = createController<MyEvents>({ debug: true });
|
|
735
|
-
*
|
|
736
|
-
* controller.on('phase-update', (data) => setPhase(data.phase));
|
|
737
|
-
* controller.onAllReady(() => console.log('Ready!'));
|
|
738
|
-
*
|
|
739
|
-
* await controller.ready;
|
|
740
|
-
* controller.send('tap', { x: 100, y: 200 });
|
|
741
|
-
* ```
|
|
742
|
-
*/
|
|
743
|
-
export declare function createController<TEvents extends EventMap = EventMap>(config?: ControllerConfig<TEvents>): Controller<TEvents>;
|
|
744
|
-
/**
|
|
745
|
-
* Options for creating mock instances.
|
|
746
|
-
*/
|
|
747
|
-
export interface MockOptions {
|
|
748
|
-
/** Initial room code */
|
|
749
|
-
roomCode?: RoomCode;
|
|
750
|
-
/** Initial controllers for Screen mock */
|
|
751
|
-
controllers?: ControllerInfo[];
|
|
752
|
-
/** My index for Controller mock */
|
|
753
|
-
myIndex?: PlayerIndex;
|
|
754
|
-
/** Auto-trigger ready state */
|
|
755
|
-
autoReady?: boolean;
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* Mock Screen for testing.
|
|
759
|
-
* Extends Screen with additional test utilities.
|
|
760
|
-
*/
|
|
761
|
-
export interface MockScreen<TEvents extends EventMap = EventMap> extends Screen<TEvents> {
|
|
762
|
-
/**
|
|
763
|
-
* Simulate receiving an event from a controller.
|
|
764
|
-
* Triggers the corresponding listener.
|
|
765
|
-
*/
|
|
766
|
-
simulateEvent<K extends EventNames<TEvents>>(playerIndex: PlayerIndex, event: K, data: EventData<TEvents, K>): void;
|
|
767
|
-
/**
|
|
768
|
-
* Simulate a controller joining.
|
|
769
|
-
* Triggers onControllerJoin callbacks.
|
|
770
|
-
*/
|
|
771
|
-
simulateControllerJoin(info: ControllerInfo): void;
|
|
772
|
-
/**
|
|
773
|
-
* Simulate a controller leaving.
|
|
774
|
-
* Triggers onControllerLeave callbacks.
|
|
775
|
-
*/
|
|
776
|
-
simulateControllerLeave(playerIndex: PlayerIndex): void;
|
|
777
|
-
/**
|
|
778
|
-
* Simulate a controller disconnecting temporarily.
|
|
779
|
-
* Triggers onControllerDisconnect callbacks and marks player as disconnected.
|
|
780
|
-
*/
|
|
781
|
-
simulateControllerDisconnect(playerIndex: PlayerIndex): void;
|
|
782
|
-
/**
|
|
783
|
-
* Simulate a controller reconnecting after a disconnect.
|
|
784
|
-
* Triggers onControllerReconnect callbacks and marks player as connected.
|
|
785
|
-
*/
|
|
786
|
-
simulateControllerReconnect(playerIndex: PlayerIndex): void;
|
|
787
|
-
/**
|
|
788
|
-
* Get all events that were broadcast.
|
|
789
|
-
* Returns array of { event, data } objects.
|
|
790
|
-
*/
|
|
791
|
-
getBroadcasts(): Array<{
|
|
792
|
-
event: string;
|
|
793
|
-
data: unknown;
|
|
794
|
-
}>;
|
|
795
|
-
/**
|
|
796
|
-
* Get all events sent to a specific controller.
|
|
797
|
-
*/
|
|
798
|
-
getSentToController(playerIndex: PlayerIndex): Array<{
|
|
799
|
-
event: string;
|
|
800
|
-
data: unknown;
|
|
801
|
-
}>;
|
|
802
|
-
/**
|
|
803
|
-
* Clear recorded events.
|
|
804
|
-
*/
|
|
805
|
-
clearRecordedEvents(): void;
|
|
806
|
-
/**
|
|
807
|
-
* Manually trigger the ready state.
|
|
808
|
-
*/
|
|
809
|
-
triggerReady(): void;
|
|
810
|
-
/**
|
|
811
|
-
* Simulate a player's character appearance being updated.
|
|
812
|
-
* Triggers onCharacterUpdated callbacks and updates the controller's appearance.
|
|
813
|
-
*/
|
|
814
|
-
simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;
|
|
815
|
-
/**
|
|
816
|
-
* Simulate the server rate-limiting an event.
|
|
817
|
-
* Triggers onRateLimited callbacks.
|
|
818
|
-
*/
|
|
819
|
-
simulateRateLimited(event: string): void;
|
|
820
|
-
/**
|
|
821
|
-
* Simulate the all-ready event (all participants signaled ready).
|
|
822
|
-
* Triggers onAllReady callbacks.
|
|
823
|
-
*/
|
|
824
|
-
simulateAllReady(): void;
|
|
825
|
-
/** Simulate an error. Triggers onError callbacks. */
|
|
826
|
-
simulateError(error: any): void;
|
|
827
|
-
}
|
|
828
|
-
/**
|
|
829
|
-
* Mock Controller for testing.
|
|
830
|
-
* Extends Controller with additional test utilities.
|
|
831
|
-
*/
|
|
832
|
-
export interface MockController<TEvents extends EventMap = EventMap> extends Controller<TEvents> {
|
|
833
|
-
/**
|
|
834
|
-
* Simulate receiving an event from the screen.
|
|
835
|
-
* Triggers the corresponding listener.
|
|
836
|
-
*/
|
|
837
|
-
simulateEvent<K extends EventNames<TEvents>>(event: K, data: EventData<TEvents, K>): void;
|
|
838
|
-
/**
|
|
839
|
-
* Get all events that were sent to the screen.
|
|
840
|
-
* Returns array of { event, data } objects.
|
|
841
|
-
*/
|
|
842
|
-
getSentEvents(): Array<{
|
|
843
|
-
event: string;
|
|
844
|
-
data: unknown;
|
|
845
|
-
}>;
|
|
846
|
-
/**
|
|
847
|
-
* Clear recorded events.
|
|
848
|
-
*/
|
|
849
|
-
clearRecordedEvents(): void;
|
|
850
|
-
/**
|
|
851
|
-
* Manually trigger the ready state.
|
|
852
|
-
*/
|
|
853
|
-
triggerReady(): void;
|
|
854
|
-
/**
|
|
855
|
-
* Simulate another player joining the room.
|
|
856
|
-
* Triggers onControllerJoin callbacks.
|
|
857
|
-
*/
|
|
858
|
-
simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void;
|
|
859
|
-
/**
|
|
860
|
-
* Simulate another player leaving the room.
|
|
861
|
-
* Triggers onControllerLeave callbacks.
|
|
862
|
-
*/
|
|
863
|
-
simulatePlayerLeave(playerIndex: PlayerIndex): void;
|
|
864
|
-
/**
|
|
865
|
-
* Simulate another player disconnecting temporarily.
|
|
866
|
-
* Triggers onControllerDisconnect callbacks.
|
|
867
|
-
*/
|
|
868
|
-
simulatePlayerDisconnect(playerIndex: PlayerIndex): void;
|
|
869
|
-
/**
|
|
870
|
-
* Simulate another player reconnecting after a disconnect.
|
|
871
|
-
* Triggers onControllerReconnect callbacks.
|
|
872
|
-
*/
|
|
873
|
-
simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void;
|
|
874
|
-
/**
|
|
875
|
-
* Simulate a player's character appearance being updated.
|
|
876
|
-
* Triggers onCharacterUpdated callbacks and updates the controller's appearance.
|
|
877
|
-
*/
|
|
878
|
-
simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;
|
|
879
|
-
/**
|
|
880
|
-
* Simulate the server rate-limiting an event.
|
|
881
|
-
* Triggers onRateLimited callbacks.
|
|
882
|
-
*/
|
|
883
|
-
simulateRateLimited(event: string): void;
|
|
884
|
-
/**
|
|
885
|
-
* Simulate the all-ready event (all participants signaled ready).
|
|
886
|
-
* Triggers onAllReady callbacks.
|
|
887
|
-
*/
|
|
888
|
-
simulateAllReady(): void;
|
|
889
|
-
/** Simulate an error. Triggers onError callbacks. */
|
|
890
|
-
simulateError(error: any): void;
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* Create a mock Screen for testing.
|
|
894
|
-
*
|
|
895
|
-
* @example
|
|
896
|
-
* ```ts
|
|
897
|
-
* const mockScreen = createMockScreen<MyEvents>({
|
|
898
|
-
* controllers: [
|
|
899
|
-
* { playerIndex: 0, nickname: 'Player 1', connected: true },
|
|
900
|
-
* { playerIndex: 1, nickname: 'Player 2', connected: true },
|
|
901
|
-
* ],
|
|
902
|
-
* });
|
|
903
|
-
*
|
|
904
|
-
* mockScreen.on('tap', (pi, data) => handleTap(pi, data));
|
|
905
|
-
* mockScreen.onAllReady(() => startGame());
|
|
906
|
-
*
|
|
907
|
-
* // Simulate player input
|
|
908
|
-
* mockScreen.simulateEvent(0, 'tap', { x: 100, y: 200 });
|
|
909
|
-
*
|
|
910
|
-
* // Check what was broadcast
|
|
911
|
-
* expect(mockScreen.getBroadcasts()).toContainEqual({
|
|
912
|
-
* event: 'score-update',
|
|
913
|
-
* data: { scores: { 0: 10 } },
|
|
914
|
-
* });
|
|
915
|
-
* ```
|
|
916
|
-
*/
|
|
917
|
-
export declare function createMockScreen<TEvents extends EventMap = EventMap>(options?: MockOptions): MockScreen<TEvents>;
|
|
918
|
-
/**
|
|
919
|
-
* Create a mock Controller for testing.
|
|
920
|
-
*
|
|
921
|
-
* @example
|
|
922
|
-
* ```ts
|
|
923
|
-
* const mockController = createMockController<MyEvents>({
|
|
924
|
-
* myIndex: 0,
|
|
925
|
-
* });
|
|
926
|
-
*
|
|
927
|
-
* mockController.on('your-turn', (data) => handleTurn(data));
|
|
928
|
-
* mockController.onAllReady(() => console.log('Ready!'));
|
|
929
|
-
*
|
|
930
|
-
* // Simulate receiving from screen
|
|
931
|
-
* mockController.simulateEvent('your-turn', { timeLimit: 30 });
|
|
932
|
-
*
|
|
933
|
-
* // Check what was sent
|
|
934
|
-
* expect(mockController.getSentEvents()).toContainEqual({
|
|
935
|
-
* event: 'answer',
|
|
936
|
-
* data: { choice: 2 },
|
|
937
|
-
* });
|
|
938
|
-
* ```
|
|
939
|
-
*/
|
|
940
|
-
export declare function createMockController<TEvents extends EventMap = EventMap>(options?: MockOptions): MockController<TEvents>;
|
|
941
|
-
/**
|
|
942
|
-
* Game metadata from game.json manifest.
|
|
943
|
-
* SYNC: Also defined in game-project/types/src/types.ts - keep in sync.
|
|
944
|
-
*/
|
|
945
|
-
export interface GameMetadata {
|
|
946
|
-
/** Unique game identifier */
|
|
947
|
-
id: string;
|
|
948
|
-
/** Display title */
|
|
949
|
-
title: string;
|
|
950
|
-
/** Game description */
|
|
951
|
-
description: string;
|
|
952
|
-
/** Minimum players required */
|
|
953
|
-
minPlayers: number;
|
|
954
|
-
/** Maximum players supported */
|
|
955
|
-
maxPlayers: number;
|
|
956
|
-
/** Game categories/tags */
|
|
957
|
-
categories: string[];
|
|
958
|
-
/** Thumbnail image URL */
|
|
959
|
-
thumbnail?: string;
|
|
960
|
-
/** Author/developer name */
|
|
961
|
-
author?: string;
|
|
962
|
-
/** Game version */
|
|
963
|
-
version?: string;
|
|
964
|
-
}
|
|
965
833
|
//# sourceMappingURL=types.d.ts.map
|