@smoregg/sdk 0.6.2 → 1.0.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/README.md +29 -38
- package/dist/cjs/controller.cjs +299 -144
- package/dist/cjs/controller.cjs.map +1 -1
- package/dist/cjs/errors.cjs +36 -0
- package/dist/cjs/errors.cjs.map +1 -0
- package/dist/cjs/events.cjs +40 -19
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/index.cjs +3 -8
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/logger.cjs +75 -0
- package/dist/cjs/logger.cjs.map +1 -0
- package/dist/cjs/screen.cjs +302 -215
- package/dist/cjs/screen.cjs.map +1 -1
- package/dist/cjs/testing.cjs +265 -22
- package/dist/cjs/testing.cjs.map +1 -1
- package/dist/cjs/transport/DirectTransport.cjs.map +1 -1
- package/dist/cjs/transport/PostMessageTransport.cjs +11 -6
- package/dist/cjs/transport/PostMessageTransport.cjs.map +1 -1
- package/dist/cjs/transport/protocol.cjs +25 -5
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/esm/controller.js +292 -136
- package/dist/esm/controller.js.map +1 -1
- package/dist/esm/errors.js +34 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/events.js +38 -18
- package/dist/esm/events.js.map +1 -1
- package/dist/esm/index.js +3 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/logger.js +73 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/screen.js +290 -202
- package/dist/esm/screen.js.map +1 -1
- package/dist/esm/testing.js +265 -22
- package/dist/esm/testing.js.map +1 -1
- package/dist/esm/transport/DirectTransport.js.map +1 -1
- package/dist/esm/transport/PostMessageTransport.js +12 -7
- package/dist/esm/transport/PostMessageTransport.js.map +1 -1
- package/dist/esm/transport/protocol.js +23 -4
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/types/controller.d.ts +1 -14
- package/dist/types/controller.d.ts.map +1 -1
- package/dist/types/errors.d.ts +45 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/events.d.ts +52 -12
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -6
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/logger.d.ts +35 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/screen.d.ts +1 -14
- package/dist/types/screen.d.ts.map +1 -1
- package/dist/types/testing.d.ts +0 -1
- package/dist/types/testing.d.ts.map +1 -1
- package/dist/types/transport/DirectTransport.d.ts +2 -1
- package/dist/types/transport/DirectTransport.d.ts.map +1 -1
- package/dist/types/transport/PostMessageTransport.d.ts +17 -2
- package/dist/types/transport/PostMessageTransport.d.ts.map +1 -1
- package/dist/types/transport/index.d.ts +2 -2
- package/dist/types/transport/index.d.ts.map +1 -1
- package/dist/types/transport/protocol.d.ts +71 -23
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/transport/types.d.ts +24 -2
- package/dist/types/transport/types.d.ts.map +1 -1
- package/dist/types/types.d.ts +298 -215
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +950 -349
- 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 +8 -13
package/dist/types/types.d.ts
CHANGED
|
@@ -18,7 +18,21 @@ export type RoomCode = string;
|
|
|
18
18
|
/**
|
|
19
19
|
* Base event map type. Extend this to define your game's events.
|
|
20
20
|
*
|
|
21
|
-
*
|
|
21
|
+
* **Important:** Event data values must be plain objects, not primitives.
|
|
22
|
+
* Primitive values (string, number, boolean) will be automatically wrapped
|
|
23
|
+
* as `{ data: <value> }` by the relay server, which breaks type safety.
|
|
24
|
+
*
|
|
25
|
+
* **Payload Size Limit:** Maximum payload size per event is 64KB. This limit
|
|
26
|
+
* is enforced by the server's genericRelay handler. Payloads exceeding this
|
|
27
|
+
* limit will be silently dropped by the server without error notification.
|
|
28
|
+
*
|
|
29
|
+
* **Type Safety Note:** Without providing an explicit generic type parameter,
|
|
30
|
+
* `createScreen()` and `createController()` default to the empty `EventMap`,
|
|
31
|
+
* which means `send()`, `broadcast()`, and `on()` accept any string as an event
|
|
32
|
+
* name and `unknown` as data -- effectively losing compile-time type checking.
|
|
33
|
+
* Always define and pass your game's event map for full type safety.
|
|
34
|
+
*
|
|
35
|
+
* @example Defining events for type safety
|
|
22
36
|
* ```ts
|
|
23
37
|
* interface MyGameEvents {
|
|
24
38
|
* // Screen receives from Controller
|
|
@@ -29,18 +43,28 @@ export type RoomCode = string;
|
|
|
29
43
|
* 'phase-update': { phase: 'lobby' | 'playing' | 'results' };
|
|
30
44
|
* 'your-turn': { timeLimit: number };
|
|
31
45
|
* }
|
|
46
|
+
*
|
|
47
|
+
* // With explicit generic -- full type safety
|
|
48
|
+
* const screen = await createScreen<MyGameEvents>({ ... });
|
|
49
|
+
* screen.broadcast('tap', { x: 1, y: 2 }); // Type-checked
|
|
50
|
+
*
|
|
51
|
+
* // Without generic -- no type safety (not recommended)
|
|
52
|
+
* const screen = await createScreen({ ... });
|
|
53
|
+
* screen.broadcastRaw('anything', 'any data'); // No checking
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example Event naming conventions
|
|
57
|
+
* ```ts
|
|
58
|
+
* // Define your game's event map for type safety:
|
|
59
|
+
* type MyEvents = {
|
|
60
|
+
* 'player-move': { x: number; y: number };
|
|
61
|
+
* 'game-action': { action: string; value: number };
|
|
62
|
+
* };
|
|
63
|
+
* // Event names: use kebab-case, no colons (:), no 'smore:' prefix
|
|
32
64
|
* ```
|
|
33
65
|
*/
|
|
34
66
|
export interface EventMap {
|
|
35
67
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Type constraint for event maps (allows interfaces without index signatures)
|
|
38
|
-
*/
|
|
39
|
-
export type EventMapConstraint = Record<string, unknown>;
|
|
40
|
-
/**
|
|
41
|
-
* Empty event map for games that don't need custom events.
|
|
42
|
-
*/
|
|
43
|
-
export type EmptyEventMap = Record<string, never>;
|
|
44
68
|
/**
|
|
45
69
|
* Extract event names from an event map.
|
|
46
70
|
*/
|
|
@@ -50,21 +74,25 @@ export type EventNames<TEvents extends EventMap> = keyof TEvents & string;
|
|
|
50
74
|
*/
|
|
51
75
|
export type EventData<TEvents extends EventMap, TEvent extends EventNames<TEvents>> = TEvents[TEvent];
|
|
52
76
|
/**
|
|
53
|
-
* Character appearance
|
|
77
|
+
* Character appearance data for player avatars.
|
|
78
|
+
*
|
|
79
|
+
* This type matches the server's CharacterDTO structure to ensure
|
|
80
|
+
* type consistency across the platform.
|
|
81
|
+
*
|
|
82
|
+
* @property id - Unique character identifier
|
|
83
|
+
* @property seed - Random seed for generating the character
|
|
84
|
+
* @property style - Character style preset identifier
|
|
85
|
+
* @property options - Additional character customization options
|
|
54
86
|
*/
|
|
55
87
|
export interface CharacterAppearance {
|
|
56
|
-
/**
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
|
|
64
|
-
/** Hair style identifier */
|
|
65
|
-
hairStyle?: string;
|
|
66
|
-
/** Outfit style identifier */
|
|
67
|
-
outfit?: string;
|
|
88
|
+
/** Unique character identifier */
|
|
89
|
+
id: string;
|
|
90
|
+
/** Random seed for generating the character */
|
|
91
|
+
seed: string;
|
|
92
|
+
/** Character style preset identifier */
|
|
93
|
+
style: string;
|
|
94
|
+
/** Additional character customization options */
|
|
95
|
+
options: Record<string, any>;
|
|
68
96
|
}
|
|
69
97
|
/**
|
|
70
98
|
* Information about a connected controller (player).
|
|
@@ -73,12 +101,27 @@ export interface CharacterAppearance {
|
|
|
73
101
|
export interface ControllerInfo {
|
|
74
102
|
/** Player's unique index (0, 1, 2, ...) */
|
|
75
103
|
readonly playerIndex: PlayerIndex;
|
|
76
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Player's chosen display name.
|
|
106
|
+
*
|
|
107
|
+
* Maps to `PlayerDTO.name` on the server side. The SDK exposes it as
|
|
108
|
+
* "nickname" for semantic clarity in game code.
|
|
109
|
+
* The mapping (`name` -> `nickname`) is handled automatically during
|
|
110
|
+
* player data deserialization in the transport layer.
|
|
111
|
+
*
|
|
112
|
+
* @see PlayerDTO.name (server) -- same value, different field name
|
|
113
|
+
*/
|
|
77
114
|
readonly nickname: string;
|
|
78
115
|
/** Whether the player is currently connected */
|
|
79
116
|
readonly connected: boolean;
|
|
80
|
-
/**
|
|
81
|
-
|
|
117
|
+
/**
|
|
118
|
+
* Optional character appearance data.
|
|
119
|
+
*
|
|
120
|
+
* Note: The server sends this as "character" in PlayerDTO.
|
|
121
|
+
* The SDK maps it to "appearance" for semantic clarity.
|
|
122
|
+
* The server may send `null` when no character is set.
|
|
123
|
+
*/
|
|
124
|
+
readonly appearance?: CharacterAppearance | null;
|
|
82
125
|
}
|
|
83
126
|
/**
|
|
84
127
|
* Handler for events received from controllers.
|
|
@@ -118,6 +161,17 @@ export type ScreenListeners<TEvents extends EventMap> = {
|
|
|
118
161
|
* });
|
|
119
162
|
* // Screen is ready here
|
|
120
163
|
* ```
|
|
164
|
+
*
|
|
165
|
+
* ## Best Practices: Callbacks vs Events
|
|
166
|
+
*
|
|
167
|
+
* - **Config callbacks** (`onReady`, `onControllerJoin`, etc.): Use for essential lifecycle events
|
|
168
|
+
* that should always be handled. These are registered once at creation and cannot be removed.
|
|
169
|
+
*
|
|
170
|
+
* - **`on()`/`off()` methods**: Use for dynamic event handlers that may need to be added/removed
|
|
171
|
+
* during gameplay. These support multiple handlers per event and can be cleaned up with `off()`.
|
|
172
|
+
*
|
|
173
|
+
* For most games, config callbacks are sufficient. Use `on()`/`off()` only when you need
|
|
174
|
+
* dynamic handler management (e.g., different handlers per game phase).
|
|
121
175
|
*/
|
|
122
176
|
export interface ScreenConfig<TEvents extends EventMap = EventMap> {
|
|
123
177
|
/**
|
|
@@ -133,13 +187,46 @@ export interface ScreenConfig<TEvents extends EventMap = EventMap> {
|
|
|
133
187
|
* Called when a controller (player) leaves the room.
|
|
134
188
|
*/
|
|
135
189
|
onControllerLeave?: (playerIndex: PlayerIndex) => void;
|
|
190
|
+
/**
|
|
191
|
+
* Called when a controller temporarily disconnects (e.g., lost WiFi).
|
|
192
|
+
* The player may reconnect — see onControllerReconnect.
|
|
193
|
+
*/
|
|
194
|
+
onControllerDisconnect?: (playerIndex: PlayerIndex) => void;
|
|
136
195
|
/**
|
|
137
196
|
* Called when a controller reconnects after a disconnect.
|
|
138
197
|
*/
|
|
139
198
|
onControllerReconnect?: (playerIndex: PlayerIndex, info: ControllerInfo) => void;
|
|
199
|
+
/**
|
|
200
|
+
* Called when a player's character appearance is updated.
|
|
201
|
+
*
|
|
202
|
+
* @param playerIndex - The player whose character changed
|
|
203
|
+
* @param appearance - The new character appearance, or null if removed/reset
|
|
204
|
+
*
|
|
205
|
+
* @note Sending an empty/null character update from the platform resets the
|
|
206
|
+
* player's appearance to null (default state). This allows players to clear
|
|
207
|
+
* their character selection.
|
|
208
|
+
*/
|
|
209
|
+
onCharacterUpdated?: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void;
|
|
210
|
+
/**
|
|
211
|
+
* Called when the server rate-limits an event from this client.
|
|
212
|
+
*
|
|
213
|
+
* @param event - The event name that was rate-limited
|
|
214
|
+
*/
|
|
215
|
+
onRateLimited?: (event: string) => void;
|
|
140
216
|
/**
|
|
141
217
|
* Event listeners for messages from controllers.
|
|
142
218
|
* Keys are event names, values are handler functions.
|
|
219
|
+
*
|
|
220
|
+
* **Relationship with on() method:**
|
|
221
|
+
* - Listeners passed in config are registered during initialization
|
|
222
|
+
* - Listeners added via on() are registered dynamically after initialization
|
|
223
|
+
* - Both types of listeners coexist and are called in registration order
|
|
224
|
+
*
|
|
225
|
+
* **Important:** Lifecycle listeners (onReady, onControllerJoin, etc.) passed
|
|
226
|
+
* in config are NOT removable via off(). They are permanent for the lifetime
|
|
227
|
+
* of the Screen instance. Only listeners added via on() can be removed via off().
|
|
228
|
+
*
|
|
229
|
+
* @see Best Practices section above for guidance on when to use `listeners` config vs `on()` method.
|
|
143
230
|
*/
|
|
144
231
|
listeners?: ScreenListeners<TEvents>;
|
|
145
232
|
/**
|
|
@@ -184,12 +271,16 @@ export interface ScreenConfig<TEvents extends EventMap = EventMap> {
|
|
|
184
271
|
* ```
|
|
185
272
|
*/
|
|
186
273
|
export interface Screen<TEvents extends EventMap = EventMap> {
|
|
187
|
-
/**
|
|
274
|
+
/**
|
|
275
|
+
* All connected controllers. Returns a new shallow copy on every access.
|
|
276
|
+
*
|
|
277
|
+
* **Performance note:** Each access creates a new array via spread.
|
|
278
|
+
* Avoid calling this in tight loops; cache the result in a local variable
|
|
279
|
+
* if you need to access it repeatedly within the same frame/tick.
|
|
280
|
+
*/
|
|
188
281
|
readonly controllers: readonly ControllerInfo[];
|
|
189
282
|
/** The room code for this game session. */
|
|
190
283
|
readonly roomCode: RoomCode;
|
|
191
|
-
/** Index of the leader/host controller (-1 if none). */
|
|
192
|
-
readonly leaderIndex: PlayerIndex;
|
|
193
284
|
/** Whether the screen is initialized and ready. */
|
|
194
285
|
readonly isReady: boolean;
|
|
195
286
|
/** Whether the screen has been destroyed. */
|
|
@@ -199,6 +290,8 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
199
290
|
*
|
|
200
291
|
* @param event - Event name (must match TEvents keys)
|
|
201
292
|
* @param data - Event data (type-safe based on event name)
|
|
293
|
+
* @note Data should be an object. Primitive values will be wrapped as `{ data: value }` by the relay server.
|
|
294
|
+
* @note Maximum payload size is 64KB. Payloads exceeding this limit will be silently dropped by the server.
|
|
202
295
|
*
|
|
203
296
|
* @example
|
|
204
297
|
* ```ts
|
|
@@ -214,9 +307,13 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
214
307
|
/**
|
|
215
308
|
* Send an event to a specific controller.
|
|
216
309
|
*
|
|
310
|
+
* Screen → Controller direction only. For Controller → Screen, see `send()`.
|
|
311
|
+
*
|
|
217
312
|
* @param playerIndex - Target controller's player index
|
|
218
313
|
* @param event - Event name (must match TEvents keys)
|
|
219
314
|
* @param data - Event data (type-safe based on event name)
|
|
315
|
+
* @note Data should be an object. Primitive values will be wrapped as `{ data: value }` by the relay server.
|
|
316
|
+
* @note Maximum payload size is 64KB. Payloads exceeding this limit will be silently dropped by the server.
|
|
220
317
|
*
|
|
221
318
|
* @example
|
|
222
319
|
* ```ts
|
|
@@ -265,6 +362,9 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
265
362
|
on<K extends EventNames<TEvents>>(event: K, handler: ScreenEventHandler<EventData<TEvents, K>>): () => void;
|
|
266
363
|
/**
|
|
267
364
|
* Add a one-time listener that auto-removes after first call.
|
|
365
|
+
*
|
|
366
|
+
* @note The handler is internally wrapped, so it cannot be removed via
|
|
367
|
+
* `off(event, originalHandler)`. Use the returned unsubscribe function instead.
|
|
268
368
|
*/
|
|
269
369
|
once<K extends EventNames<TEvents>>(event: K, handler: ScreenEventHandler<EventData<TEvents, K>>): () => void;
|
|
270
370
|
/**
|
|
@@ -276,14 +376,18 @@ export interface Screen<TEvents extends EventMap = EventMap> {
|
|
|
276
376
|
* Returns undefined if not found.
|
|
277
377
|
*/
|
|
278
378
|
getController(playerIndex: PlayerIndex): ControllerInfo | undefined;
|
|
279
|
-
/**
|
|
280
|
-
* Check if a player index is the leader.
|
|
281
|
-
*/
|
|
282
|
-
isLeader(playerIndex: PlayerIndex): boolean;
|
|
283
379
|
/**
|
|
284
380
|
* Get the number of connected controllers.
|
|
285
381
|
*/
|
|
286
382
|
getControllerCount(): number;
|
|
383
|
+
/**
|
|
384
|
+
* Check if there is at least one connected controller.
|
|
385
|
+
* Useful for detecting when all players have disconnected
|
|
386
|
+
* (e.g., to pause the game or show a waiting screen).
|
|
387
|
+
*
|
|
388
|
+
* Screen-only method. Controller instances can check their own connection status directly.
|
|
389
|
+
*/
|
|
390
|
+
hasAnyConnectedControllers(): boolean;
|
|
287
391
|
/**
|
|
288
392
|
* Clean up all resources and disconnect.
|
|
289
393
|
* Call this when unmounting/destroying your game.
|
|
@@ -343,9 +447,62 @@ export interface ControllerConfig<TEvents extends EventMap = EventMap> {
|
|
|
343
447
|
* Called when another player leaves the room.
|
|
344
448
|
*/
|
|
345
449
|
onControllerLeave?: (playerIndex: PlayerIndex) => void;
|
|
450
|
+
/**
|
|
451
|
+
* Called when another player temporarily disconnects.
|
|
452
|
+
*/
|
|
453
|
+
onControllerDisconnect?: (playerIndex: PlayerIndex) => void;
|
|
454
|
+
/**
|
|
455
|
+
* Called when another player reconnects after a disconnect.
|
|
456
|
+
*/
|
|
457
|
+
onControllerReconnect?: (playerIndex: PlayerIndex, info: ControllerInfo) => void;
|
|
458
|
+
/**
|
|
459
|
+
* Called when a player's character appearance is updated.
|
|
460
|
+
*
|
|
461
|
+
* @param playerIndex - The player whose character changed
|
|
462
|
+
* @param appearance - The new character appearance, or null if removed/reset
|
|
463
|
+
*
|
|
464
|
+
* @note Sending an empty/null character update from the platform resets the
|
|
465
|
+
* player's appearance to null (default state). This allows players to clear
|
|
466
|
+
* their character selection.
|
|
467
|
+
*/
|
|
468
|
+
onCharacterUpdated?: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void;
|
|
469
|
+
/**
|
|
470
|
+
* Called when the server rate-limits an event from this client.
|
|
471
|
+
*
|
|
472
|
+
* @param event - The event name that was rate-limited
|
|
473
|
+
*/
|
|
474
|
+
onRateLimited?: (event: string) => void;
|
|
475
|
+
/**
|
|
476
|
+
* Called when the host/screen disconnects.
|
|
477
|
+
* The game may become unresponsive until the host reconnects.
|
|
478
|
+
*
|
|
479
|
+
* @experimental This callback is reserved for future use and currently non-functional.
|
|
480
|
+
* The server emits `smore:host-disconnected` but it is not yet forwarded to SDK games
|
|
481
|
+
* through the IframeGameBridge GAME_FACING_EVENTS allowlist. Setting this callback
|
|
482
|
+
* will produce a runtime warning. It will become functional in a future SDK release.
|
|
483
|
+
*/
|
|
484
|
+
onHostDisconnect?: () => void;
|
|
485
|
+
/**
|
|
486
|
+
* Called when the host/screen reconnects after a disconnect.
|
|
487
|
+
*
|
|
488
|
+
* @experimental This callback is reserved for future use and currently non-functional.
|
|
489
|
+
* The server emits `smore:host-reconnected` but it is not yet forwarded to SDK games
|
|
490
|
+
* through the IframeGameBridge GAME_FACING_EVENTS allowlist. Setting this callback
|
|
491
|
+
* will produce a runtime warning. It will become functional in a future SDK release.
|
|
492
|
+
*/
|
|
493
|
+
onHostReconnect?: () => void;
|
|
346
494
|
/**
|
|
347
495
|
* Event listeners for messages from the screen.
|
|
348
496
|
* Keys are event names, values are handler functions.
|
|
497
|
+
*
|
|
498
|
+
* **Relationship with on() method:**
|
|
499
|
+
* - Listeners passed in config are registered during initialization
|
|
500
|
+
* - Listeners added via on() are registered dynamically after initialization
|
|
501
|
+
* - Both types of listeners coexist and are called in registration order
|
|
502
|
+
*
|
|
503
|
+
* **Important:** Lifecycle listeners (onReady, onControllerJoin, etc.) passed
|
|
504
|
+
* in config are NOT removable via off(). They are permanent for the lifetime
|
|
505
|
+
* of the Controller instance. Only listeners added via on() can be removed via off().
|
|
349
506
|
*/
|
|
350
507
|
listeners?: ControllerListeners<TEvents>;
|
|
351
508
|
/**
|
|
@@ -380,29 +537,46 @@ export interface ControllerConfig<TEvents extends EventMap = EventMap> {
|
|
|
380
537
|
*
|
|
381
538
|
* // Send input to screen
|
|
382
539
|
* controller.send('tap', { x: 100, y: 200 });
|
|
383
|
-
*
|
|
384
|
-
* // Check if I'm the leader
|
|
385
|
-
* if (controller.isLeader) {
|
|
386
|
-
* // Show leader-only controls
|
|
387
|
-
* }
|
|
388
540
|
* ```
|
|
541
|
+
*
|
|
542
|
+
* ## Controller API Design Note:
|
|
543
|
+
*
|
|
544
|
+
* Controller only has `send()` (no `broadcast()` or `gameOver()`).
|
|
545
|
+
* This is intentional: Controller-to-Controller (C2C) communication is not supported.
|
|
546
|
+
* All coordination between controllers must go through the Screen.
|
|
547
|
+
*
|
|
548
|
+
* Flow: Controller.send() → Screen receives → Screen.broadcast() or Screen.sendToController()
|
|
389
549
|
*/
|
|
390
550
|
export interface Controller<TEvents extends EventMap = EventMap> {
|
|
391
551
|
/** My player index (0, 1, 2, ...). */
|
|
392
552
|
readonly myIndex: PlayerIndex;
|
|
393
|
-
/** Whether I am the room leader/host. */
|
|
394
|
-
readonly isLeader: boolean;
|
|
395
553
|
/** The room code for this game session. */
|
|
396
554
|
readonly roomCode: RoomCode;
|
|
397
555
|
/** Whether the controller is initialized and ready. */
|
|
398
556
|
readonly isReady: boolean;
|
|
399
557
|
/** Whether the controller has been destroyed. */
|
|
400
558
|
readonly isDestroyed: boolean;
|
|
559
|
+
/**
|
|
560
|
+
* Read-only list of all known controllers (players) in the room.
|
|
561
|
+
* Returns full ControllerInfo including playerIndex, nickname, connected status, and appearance.
|
|
562
|
+
* Consistent with Screen's `controllers` property.
|
|
563
|
+
*
|
|
564
|
+
* **Performance note:** Each access creates a new shallow copy array.
|
|
565
|
+
* Cache the result in a local variable if you need to access it repeatedly.
|
|
566
|
+
*/
|
|
567
|
+
readonly controllers: readonly ControllerInfo[];
|
|
568
|
+
/**
|
|
569
|
+
* Returns the number of currently connected players.
|
|
570
|
+
*/
|
|
571
|
+
getControllerCount(): number;
|
|
401
572
|
/**
|
|
402
573
|
* Send an event to the screen.
|
|
403
574
|
*
|
|
575
|
+
* Controller → Screen direction only. For Screen → Controller, see `sendToController()`.
|
|
576
|
+
*
|
|
404
577
|
* @param event - Event name (must match TEvents keys)
|
|
405
578
|
* @param data - Event data (type-safe based on event name)
|
|
579
|
+
* @note Maximum payload size is 64KB. Payloads exceeding this limit will be silently dropped by the server.
|
|
406
580
|
*
|
|
407
581
|
* @example
|
|
408
582
|
* ```ts
|
|
@@ -423,6 +597,11 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
423
597
|
* @param handler - Handler function (data) => void
|
|
424
598
|
* @returns Cleanup function to remove the listener
|
|
425
599
|
*
|
|
600
|
+
* @note Events from screen.broadcast() do not include playerIndex.
|
|
601
|
+
* The handler signature is `(data: D)` without playerIndex context.
|
|
602
|
+
* If you need to know which player triggered the broadcast, include
|
|
603
|
+
* that information in the data object from the Screen side.
|
|
604
|
+
*
|
|
426
605
|
* @example
|
|
427
606
|
* ```ts
|
|
428
607
|
* const unsubscribe = controller.on('phase-update', (data) => {
|
|
@@ -436,6 +615,9 @@ export interface Controller<TEvents extends EventMap = EventMap> {
|
|
|
436
615
|
on<K extends EventNames<TEvents>>(event: K, handler: ControllerEventHandler<EventData<TEvents, K>>): () => void;
|
|
437
616
|
/**
|
|
438
617
|
* Add a one-time listener that auto-removes after first call.
|
|
618
|
+
*
|
|
619
|
+
* @note The handler is internally wrapped, so it cannot be removed via
|
|
620
|
+
* `off(event, originalHandler)`. Use the returned unsubscribe function instead.
|
|
439
621
|
*/
|
|
440
622
|
once<K extends EventNames<TEvents>>(event: K, handler: ControllerEventHandler<EventData<TEvents, K>>): () => void;
|
|
441
623
|
/**
|
|
@@ -479,20 +661,6 @@ export interface SmoreError {
|
|
|
479
661
|
/** Additional context */
|
|
480
662
|
details?: Record<string, unknown>;
|
|
481
663
|
}
|
|
482
|
-
/**
|
|
483
|
-
* Custom error class for SDK errors.
|
|
484
|
-
*/
|
|
485
|
-
export declare class SmoreSDKError extends Error {
|
|
486
|
-
readonly code: SmoreErrorCode;
|
|
487
|
-
readonly cause?: Error;
|
|
488
|
-
readonly details?: Record<string, unknown>;
|
|
489
|
-
constructor(code: SmoreErrorCode, message: string, options?: {
|
|
490
|
-
cause?: Error;
|
|
491
|
-
details?: Record<string, unknown>;
|
|
492
|
-
});
|
|
493
|
-
/** Convert to SmoreError interface */
|
|
494
|
-
toSmoreError(): SmoreError;
|
|
495
|
-
}
|
|
496
664
|
/**
|
|
497
665
|
* Log levels for debug output.
|
|
498
666
|
*/
|
|
@@ -522,6 +690,10 @@ export interface DebugOptions {
|
|
|
522
690
|
* Returns a Promise that resolves when the screen is ready.
|
|
523
691
|
* You can also use the onReady callback in config for immediate instantiation.
|
|
524
692
|
*
|
|
693
|
+
* The return type is a Promise with an `instance` property for dual access:
|
|
694
|
+
* - Await the Promise to get the instance after initialization completes
|
|
695
|
+
* - Access `instance` property for synchronous access before initialization
|
|
696
|
+
*
|
|
525
697
|
* @template TEvents - Event map type for type-safe events
|
|
526
698
|
* @param config - Screen configuration
|
|
527
699
|
* @returns Promise that resolves to the Screen instance when ready
|
|
@@ -556,6 +728,10 @@ export declare function createScreen<TEvents extends EventMap = EventMap>(config
|
|
|
556
728
|
* Returns a Promise that resolves when the controller is ready.
|
|
557
729
|
* You can also use the onReady callback in config for immediate instantiation.
|
|
558
730
|
*
|
|
731
|
+
* The return type is a Promise with an `instance` property for dual access:
|
|
732
|
+
* - Await the Promise to get the instance after initialization completes
|
|
733
|
+
* - Access `instance` property for synchronous access before initialization
|
|
734
|
+
*
|
|
559
735
|
* @template TEvents - Event map type for type-safe events
|
|
560
736
|
* @param config - Controller configuration
|
|
561
737
|
* @returns Promise that resolves to the Controller instance when ready
|
|
@@ -594,10 +770,24 @@ export interface MockOptions {
|
|
|
594
770
|
controllers?: ControllerInfo[];
|
|
595
771
|
/** My index for Controller mock */
|
|
596
772
|
myIndex?: PlayerIndex;
|
|
597
|
-
/** Am I leader for Controller mock */
|
|
598
|
-
isLeader?: boolean;
|
|
599
773
|
/** Auto-trigger onReady */
|
|
600
774
|
autoReady?: boolean;
|
|
775
|
+
/** Called when ready is triggered */
|
|
776
|
+
onReady?: () => void;
|
|
777
|
+
/** Called when a controller joins */
|
|
778
|
+
onControllerJoin?: (playerIndex: PlayerIndex, info: ControllerInfo) => void;
|
|
779
|
+
/** Called when a controller leaves */
|
|
780
|
+
onControllerLeave?: (playerIndex: PlayerIndex) => void;
|
|
781
|
+
/** Called when a controller disconnects */
|
|
782
|
+
onControllerDisconnect?: (playerIndex: PlayerIndex) => void;
|
|
783
|
+
/** Called when a controller reconnects */
|
|
784
|
+
onControllerReconnect?: (playerIndex: PlayerIndex, info: ControllerInfo) => void;
|
|
785
|
+
/** Called when a player's character appearance is updated */
|
|
786
|
+
onCharacterUpdated?: (playerIndex: PlayerIndex, appearance: CharacterAppearance | null) => void;
|
|
787
|
+
/** Called when the server rate-limits an event from this client */
|
|
788
|
+
onRateLimited?: (event: string) => void;
|
|
789
|
+
/** Called when an error occurs */
|
|
790
|
+
onError?: (error: SmoreError) => void;
|
|
601
791
|
}
|
|
602
792
|
/**
|
|
603
793
|
* Mock Screen for testing.
|
|
@@ -619,6 +809,16 @@ export interface MockScreen<TEvents extends EventMap = EventMap> extends Screen<
|
|
|
619
809
|
* Triggers onControllerLeave callback.
|
|
620
810
|
*/
|
|
621
811
|
simulateControllerLeave(playerIndex: PlayerIndex): void;
|
|
812
|
+
/**
|
|
813
|
+
* Simulate a controller disconnecting temporarily.
|
|
814
|
+
* Triggers onControllerDisconnect callback and marks player as disconnected.
|
|
815
|
+
*/
|
|
816
|
+
simulateControllerDisconnect(playerIndex: PlayerIndex): void;
|
|
817
|
+
/**
|
|
818
|
+
* Simulate a controller reconnecting after a disconnect.
|
|
819
|
+
* Triggers onControllerReconnect callback and marks player as connected.
|
|
820
|
+
*/
|
|
821
|
+
simulateControllerReconnect(playerIndex: PlayerIndex): void;
|
|
622
822
|
/**
|
|
623
823
|
* Get all events that were broadcast.
|
|
624
824
|
* Returns array of { event, data } objects.
|
|
@@ -642,6 +842,18 @@ export interface MockScreen<TEvents extends EventMap = EventMap> extends Screen<
|
|
|
642
842
|
* Manually trigger the ready state.
|
|
643
843
|
*/
|
|
644
844
|
triggerReady(): void;
|
|
845
|
+
/**
|
|
846
|
+
* Simulate a player's character appearance being updated.
|
|
847
|
+
* Triggers onCharacterUpdated callback and updates the controller's appearance.
|
|
848
|
+
*/
|
|
849
|
+
simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;
|
|
850
|
+
/**
|
|
851
|
+
* Simulate the server rate-limiting an event.
|
|
852
|
+
* Triggers onRateLimited callback.
|
|
853
|
+
*/
|
|
854
|
+
simulateRateLimited(event: string): void;
|
|
855
|
+
/** Simulate an error. Triggers onError callback. */
|
|
856
|
+
simulateError(error: any): void;
|
|
645
857
|
}
|
|
646
858
|
/**
|
|
647
859
|
* Mock Controller for testing.
|
|
@@ -670,9 +882,37 @@ export interface MockController<TEvents extends EventMap = EventMap> extends Con
|
|
|
670
882
|
*/
|
|
671
883
|
triggerReady(): void;
|
|
672
884
|
/**
|
|
673
|
-
*
|
|
885
|
+
* Simulate another player joining the room.
|
|
886
|
+
* Triggers onControllerJoin callback.
|
|
887
|
+
*/
|
|
888
|
+
simulatePlayerJoin(playerIndex: PlayerIndex, info: ControllerInfo): void;
|
|
889
|
+
/**
|
|
890
|
+
* Simulate another player leaving the room.
|
|
891
|
+
* Triggers onControllerLeave callback.
|
|
892
|
+
*/
|
|
893
|
+
simulatePlayerLeave(playerIndex: PlayerIndex): void;
|
|
894
|
+
/**
|
|
895
|
+
* Simulate another player disconnecting temporarily.
|
|
896
|
+
* Triggers onControllerDisconnect callback.
|
|
674
897
|
*/
|
|
675
|
-
|
|
898
|
+
simulatePlayerDisconnect(playerIndex: PlayerIndex): void;
|
|
899
|
+
/**
|
|
900
|
+
* Simulate another player reconnecting after a disconnect.
|
|
901
|
+
* Triggers onControllerReconnect callback.
|
|
902
|
+
*/
|
|
903
|
+
simulatePlayerReconnect(playerIndex: PlayerIndex, info: ControllerInfo): void;
|
|
904
|
+
/**
|
|
905
|
+
* Simulate a player's character appearance being updated.
|
|
906
|
+
* Triggers onCharacterUpdated callback and updates the controller's appearance.
|
|
907
|
+
*/
|
|
908
|
+
simulateCharacterUpdate(playerIndex: PlayerIndex, appearance: CharacterAppearance | null): void;
|
|
909
|
+
/**
|
|
910
|
+
* Simulate the server rate-limiting an event.
|
|
911
|
+
* Triggers onRateLimited callback.
|
|
912
|
+
*/
|
|
913
|
+
simulateRateLimited(event: string): void;
|
|
914
|
+
/** Simulate an error. Triggers onError callback. */
|
|
915
|
+
simulateError(error: any): void;
|
|
676
916
|
}
|
|
677
917
|
/**
|
|
678
918
|
* Create a mock Screen for testing.
|
|
@@ -704,7 +944,6 @@ export declare function createMockScreen<TEvents extends EventMap = EventMap>(op
|
|
|
704
944
|
* ```ts
|
|
705
945
|
* const mockController = createMockController<MyEvents>({
|
|
706
946
|
* myIndex: 0,
|
|
707
|
-
* isLeader: true,
|
|
708
947
|
* });
|
|
709
948
|
*
|
|
710
949
|
* // Simulate receiving from screen
|
|
@@ -718,81 +957,9 @@ export declare function createMockScreen<TEvents extends EventMap = EventMap>(op
|
|
|
718
957
|
* ```
|
|
719
958
|
*/
|
|
720
959
|
export declare function createMockController<TEvents extends EventMap = EventMap>(options?: MockOptions): MockController<TEvents>;
|
|
721
|
-
/**
|
|
722
|
-
* Transport event handler type.
|
|
723
|
-
*/
|
|
724
|
-
export type TransportEventHandler = (...args: unknown[]) => void;
|
|
725
|
-
/**
|
|
726
|
-
* Abstract transport interface for communication.
|
|
727
|
-
* Used internally but exposed for custom integrations.
|
|
728
|
-
*/
|
|
729
|
-
export interface Transport {
|
|
730
|
-
/** Emit an event with optional data */
|
|
731
|
-
emit(event: string, ...args: unknown[]): void;
|
|
732
|
-
/** Register an event handler */
|
|
733
|
-
on(event: string, handler: TransportEventHandler): void;
|
|
734
|
-
/** Remove an event handler */
|
|
735
|
-
off(event: string, handler?: TransportEventHandler): void;
|
|
736
|
-
}
|
|
737
|
-
/**
|
|
738
|
-
* Direct transport options (for bundled games with Socket.IO).
|
|
739
|
-
*/
|
|
740
|
-
export interface DirectTransportOptions {
|
|
741
|
-
socket: unknown;
|
|
742
|
-
}
|
|
743
|
-
/**
|
|
744
|
-
* PostMessage transport options (for iframe games).
|
|
745
|
-
*/
|
|
746
|
-
export interface PostMessageTransportOptions {
|
|
747
|
-
/** Parent window origin for validation */
|
|
748
|
-
parentOrigin?: string;
|
|
749
|
-
}
|
|
750
|
-
/**
|
|
751
|
-
* @deprecated Use ControllerInfo instead
|
|
752
|
-
*/
|
|
753
|
-
export type Player = ControllerInfo;
|
|
754
|
-
/**
|
|
755
|
-
* @deprecated Use ControllerInfo instead
|
|
756
|
-
*/
|
|
757
|
-
export type SmoreScreenPlayer = ControllerInfo;
|
|
758
|
-
/**
|
|
759
|
-
* @deprecated Use ControllerInfo instead
|
|
760
|
-
*/
|
|
761
|
-
export type SmoreHostPlayer = ControllerInfo;
|
|
762
|
-
/**
|
|
763
|
-
* @deprecated Use ControllerInfo instead
|
|
764
|
-
*/
|
|
765
|
-
export type SmoreControllerInfo = ControllerInfo;
|
|
766
|
-
/**
|
|
767
|
-
* @deprecated Use ControllerInfo instead
|
|
768
|
-
*/
|
|
769
|
-
export type SmorePlayerInfo = ControllerInfo;
|
|
770
|
-
/**
|
|
771
|
-
* @deprecated Use ScreenConfig instead
|
|
772
|
-
*/
|
|
773
|
-
export type SmoreScreenConfig<T extends EventMap = EventMap> = ScreenConfig<T>;
|
|
774
|
-
/**
|
|
775
|
-
* @deprecated Use ScreenConfig instead
|
|
776
|
-
*/
|
|
777
|
-
export type SmoreHostConfig<T extends EventMap = EventMap> = ScreenConfig<T>;
|
|
778
|
-
/**
|
|
779
|
-
* @deprecated Use ControllerConfig instead
|
|
780
|
-
*/
|
|
781
|
-
export type SmoreControllerConfig<T extends EventMap = EventMap> = ControllerConfig<T>;
|
|
782
|
-
/**
|
|
783
|
-
* @deprecated Use ControllerConfig instead
|
|
784
|
-
*/
|
|
785
|
-
export type SmorePlayerConfig<T extends EventMap = EventMap> = ControllerConfig<T>;
|
|
786
|
-
/**
|
|
787
|
-
* @deprecated Use Screen instead
|
|
788
|
-
*/
|
|
789
|
-
export type SmoreScreen<T extends EventMap = EventMap> = Screen<T>;
|
|
790
|
-
/**
|
|
791
|
-
* @deprecated Use Controller instead
|
|
792
|
-
*/
|
|
793
|
-
export type SmoreController<T extends EventMap = EventMap> = Controller<T>;
|
|
794
960
|
/**
|
|
795
961
|
* Game metadata from game.json manifest.
|
|
962
|
+
* SYNC: Also defined in game-project/types/src/types.ts - keep in sync.
|
|
796
963
|
*/
|
|
797
964
|
export interface GameMetadata {
|
|
798
965
|
/** Unique game identifier */
|
|
@@ -814,88 +981,4 @@ export interface GameMetadata {
|
|
|
814
981
|
/** Game version */
|
|
815
982
|
version?: string;
|
|
816
983
|
}
|
|
817
|
-
/**
|
|
818
|
-
* Generic game state type.
|
|
819
|
-
* Use a more specific type in your game.
|
|
820
|
-
*/
|
|
821
|
-
export type GameState = Record<string, unknown>;
|
|
822
|
-
/**
|
|
823
|
-
* Input callback type for legacy compatibility.
|
|
824
|
-
* @deprecated Use ScreenEventHandler instead
|
|
825
|
-
*/
|
|
826
|
-
export type InputCallback = (playerIndex: PlayerIndex, data?: unknown) => void;
|
|
827
|
-
/**
|
|
828
|
-
* Props for TapButton component.
|
|
829
|
-
*/
|
|
830
|
-
export interface TapButtonProps {
|
|
831
|
-
/** Called when button is tapped */
|
|
832
|
-
onTap: () => void;
|
|
833
|
-
/** Button content */
|
|
834
|
-
children?: React.ReactNode;
|
|
835
|
-
/** Additional CSS classes */
|
|
836
|
-
className?: string;
|
|
837
|
-
/** Disable the button */
|
|
838
|
-
disabled?: boolean;
|
|
839
|
-
}
|
|
840
|
-
/**
|
|
841
|
-
* Props for HoldButton component.
|
|
842
|
-
*/
|
|
843
|
-
export interface HoldButtonProps {
|
|
844
|
-
/** Called when hold starts */
|
|
845
|
-
onHoldStart: () => void;
|
|
846
|
-
/** Called when hold ends */
|
|
847
|
-
onHoldEnd: () => void;
|
|
848
|
-
/** Button content */
|
|
849
|
-
children?: React.ReactNode;
|
|
850
|
-
/** Additional CSS classes */
|
|
851
|
-
className?: string;
|
|
852
|
-
/** Disable the button */
|
|
853
|
-
disabled?: boolean;
|
|
854
|
-
}
|
|
855
|
-
/**
|
|
856
|
-
* Direction type for directional inputs.
|
|
857
|
-
*/
|
|
858
|
-
export type Direction = 'up' | 'down' | 'left' | 'right';
|
|
859
|
-
/**
|
|
860
|
-
* Props for DirectionPad component.
|
|
861
|
-
*/
|
|
862
|
-
export interface DirectionPadProps {
|
|
863
|
-
/** Called when a direction is pressed */
|
|
864
|
-
onDirection: (direction: Direction) => void;
|
|
865
|
-
/** Show only left/right buttons */
|
|
866
|
-
leftRightOnly?: boolean;
|
|
867
|
-
/** Show only up/down buttons */
|
|
868
|
-
upDownOnly?: boolean;
|
|
869
|
-
/** Additional CSS classes */
|
|
870
|
-
className?: string;
|
|
871
|
-
}
|
|
872
|
-
/**
|
|
873
|
-
* Props for SwipeArea component.
|
|
874
|
-
*/
|
|
875
|
-
export interface SwipeAreaProps {
|
|
876
|
-
/** Called when a swipe is detected */
|
|
877
|
-
onSwipe: (direction: Direction) => void;
|
|
878
|
-
/** Minimum swipe distance in pixels */
|
|
879
|
-
threshold?: number;
|
|
880
|
-
/** Area content */
|
|
881
|
-
children?: React.ReactNode;
|
|
882
|
-
/** Additional CSS classes */
|
|
883
|
-
className?: string;
|
|
884
|
-
}
|
|
885
|
-
/**
|
|
886
|
-
* Namespace for Screen-related types.
|
|
887
|
-
*/
|
|
888
|
-
export declare namespace Screen {
|
|
889
|
-
type Config<T extends EventMap = EventMap> = ScreenConfig<T>;
|
|
890
|
-
type Listeners<T extends EventMap = EventMap> = ScreenListeners<T>;
|
|
891
|
-
type EventHandler<T = unknown> = ScreenEventHandler<T>;
|
|
892
|
-
}
|
|
893
|
-
/**
|
|
894
|
-
* Namespace for Controller-related types.
|
|
895
|
-
*/
|
|
896
|
-
export declare namespace Controller {
|
|
897
|
-
type Config<T extends EventMap = EventMap> = ControllerConfig<T>;
|
|
898
|
-
type Listeners<T extends EventMap = EventMap> = ControllerListeners<T>;
|
|
899
|
-
type EventHandler<T = unknown> = ControllerEventHandler<T>;
|
|
900
|
-
}
|
|
901
984
|
//# sourceMappingURL=types.d.ts.map
|