@smoregg/sdk 1.3.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 +342 -193
- 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 +336 -238
- 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 +269 -197
- 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 +344 -195
- 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 +338 -240
- 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 +269 -197
- 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 +22 -43
- 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 +15 -19
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/screen.d.ts +26 -37
- 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 +78 -3
- 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 +391 -540
- package/dist/types/types.d.ts.map +1 -1
- package/dist/umd/smore-sdk.umd.js +742 -952
- 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/esm/testing.js
CHANGED
|
@@ -1,39 +1,30 @@
|
|
|
1
1
|
import { validateEventName } from './events.js';
|
|
2
|
+
import { SmoreSDKError } from './errors.js';
|
|
2
3
|
|
|
3
4
|
function createMockScreen(options = {}) {
|
|
4
5
|
const {
|
|
5
6
|
roomCode = "TEST",
|
|
6
7
|
controllers: initialControllers = [],
|
|
7
|
-
autoReady = true
|
|
8
|
-
onControllerJoin: onJoinCb,
|
|
9
|
-
onControllerLeave: onLeaveCb,
|
|
10
|
-
onControllerDisconnect: onDisconnectCb,
|
|
11
|
-
onControllerReconnect: onReconnectCb,
|
|
12
|
-
onCharacterUpdated: onCharacterUpdatedCb,
|
|
13
|
-
onRateLimited: onRateLimitedCb,
|
|
14
|
-
onReady: onReadyCb,
|
|
15
|
-
onError: onErrorCb
|
|
8
|
+
autoReady = true
|
|
16
9
|
} = options;
|
|
17
10
|
let _controllers = [...initialControllers];
|
|
18
11
|
let _isReady = false;
|
|
12
|
+
let _isConnected = false;
|
|
19
13
|
let _isDestroyed = false;
|
|
20
14
|
const listeners = /* @__PURE__ */ new Map();
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
onRateLimitedCallback = onRateLimitedCb;
|
|
35
|
-
onReadyCallback = onReadyCb;
|
|
36
|
-
onErrorCallback = onErrorCb;
|
|
15
|
+
const _onAllReadyCallbacks = /* @__PURE__ */ new Set();
|
|
16
|
+
const _onControllerJoinCallbacks = /* @__PURE__ */ new Set();
|
|
17
|
+
const _onControllerLeaveCallbacks = /* @__PURE__ */ new Set();
|
|
18
|
+
const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
|
|
19
|
+
const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
|
|
20
|
+
const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
21
|
+
const _onErrorCallbacks = /* @__PURE__ */ new Set();
|
|
22
|
+
const _onConnectionChangeCallbacks = /* @__PURE__ */ new Set();
|
|
23
|
+
let _allReadyFired = false;
|
|
24
|
+
let _readyResolve;
|
|
25
|
+
const _readyPromise = new Promise((resolve) => {
|
|
26
|
+
_readyResolve = resolve;
|
|
27
|
+
});
|
|
37
28
|
const broadcasts = [];
|
|
38
29
|
const sends = [];
|
|
39
30
|
const screen = {
|
|
@@ -50,75 +41,131 @@ function createMockScreen(options = {}) {
|
|
|
50
41
|
get isDestroyed() {
|
|
51
42
|
return _isDestroyed;
|
|
52
43
|
},
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
get isConnected() {
|
|
45
|
+
return _isConnected;
|
|
46
|
+
},
|
|
47
|
+
get protocolVersion() {
|
|
48
|
+
return 1;
|
|
49
|
+
},
|
|
50
|
+
get ready() {
|
|
51
|
+
return _readyPromise;
|
|
52
|
+
},
|
|
53
|
+
// === Lifecycle Methods ===
|
|
54
|
+
onAllReady(callback) {
|
|
55
|
+
if (_allReadyFired) {
|
|
56
|
+
callback();
|
|
60
57
|
}
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
_onAllReadyCallbacks.add(callback);
|
|
59
|
+
return () => {
|
|
60
|
+
_onAllReadyCallbacks.delete(callback);
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
onControllerJoin(callback) {
|
|
64
|
+
_onControllerJoinCallbacks.add(callback);
|
|
65
|
+
return () => {
|
|
66
|
+
_onControllerJoinCallbacks.delete(callback);
|
|
67
|
+
};
|
|
68
|
+
},
|
|
69
|
+
onControllerLeave(callback) {
|
|
70
|
+
_onControllerLeaveCallbacks.add(callback);
|
|
71
|
+
return () => {
|
|
72
|
+
_onControllerLeaveCallbacks.delete(callback);
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
onControllerDisconnect(callback) {
|
|
76
|
+
_onControllerDisconnectCallbacks.add(callback);
|
|
77
|
+
return () => {
|
|
78
|
+
_onControllerDisconnectCallbacks.delete(callback);
|
|
79
|
+
};
|
|
80
|
+
},
|
|
81
|
+
onControllerReconnect(callback) {
|
|
82
|
+
_onControllerReconnectCallbacks.add(callback);
|
|
83
|
+
return () => {
|
|
84
|
+
_onControllerReconnectCallbacks.delete(callback);
|
|
85
|
+
};
|
|
86
|
+
},
|
|
87
|
+
onCharacterUpdated(callback) {
|
|
88
|
+
_onCharacterUpdatedCallbacks.add(callback);
|
|
89
|
+
return () => {
|
|
90
|
+
_onCharacterUpdatedCallbacks.delete(callback);
|
|
91
|
+
};
|
|
92
|
+
},
|
|
93
|
+
onError(callback) {
|
|
94
|
+
_onErrorCallbacks.add(callback);
|
|
95
|
+
return () => {
|
|
96
|
+
_onErrorCallbacks.delete(callback);
|
|
97
|
+
};
|
|
63
98
|
},
|
|
64
|
-
|
|
99
|
+
onConnectionChange(callback) {
|
|
100
|
+
_onConnectionChangeCallbacks.add(callback);
|
|
101
|
+
return () => {
|
|
102
|
+
_onConnectionChangeCallbacks.delete(callback);
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
// === Communication Methods ===
|
|
106
|
+
broadcast(event, data) {
|
|
65
107
|
if (_isDestroyed) {
|
|
66
|
-
throw new
|
|
108
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call broadcast() after destroy()");
|
|
67
109
|
}
|
|
68
110
|
if (!_isReady) {
|
|
69
|
-
throw new
|
|
111
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call broadcast() before screen is ready");
|
|
70
112
|
}
|
|
71
113
|
validateEventName(event);
|
|
72
114
|
broadcasts.push({ event, data });
|
|
73
115
|
},
|
|
74
116
|
sendToController(playerIndex, event, data) {
|
|
75
117
|
if (_isDestroyed) {
|
|
76
|
-
throw new
|
|
118
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call sendToController() after destroy()");
|
|
77
119
|
}
|
|
78
120
|
if (!_isReady) {
|
|
79
|
-
throw new
|
|
121
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call sendToController() before screen is ready");
|
|
80
122
|
}
|
|
81
123
|
validateEventName(event);
|
|
82
124
|
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
83
|
-
throw new
|
|
84
|
-
}
|
|
85
|
-
sends.push({ playerIndex, event, data });
|
|
86
|
-
},
|
|
87
|
-
sendToControllerRaw(playerIndex, event, data) {
|
|
88
|
-
if (_isDestroyed) {
|
|
89
|
-
throw new Error("Cannot send: screen is destroyed");
|
|
90
|
-
}
|
|
91
|
-
if (!_isReady) {
|
|
92
|
-
throw new Error("Cannot send: screen is not ready");
|
|
93
|
-
}
|
|
94
|
-
validateEventName(event);
|
|
95
|
-
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
96
|
-
throw new Error(`Invalid player index: ${playerIndex}`);
|
|
125
|
+
throw new SmoreSDKError("INVALID_PLAYER", `No controller found with player index ${playerIndex}`);
|
|
97
126
|
}
|
|
98
127
|
sends.push({ playerIndex, event, data });
|
|
99
128
|
},
|
|
100
129
|
// === Game Lifecycle ===
|
|
101
130
|
gameOver(results) {
|
|
102
131
|
if (_isDestroyed) {
|
|
103
|
-
throw new
|
|
132
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call gameOver() after destroy()");
|
|
104
133
|
}
|
|
105
134
|
if (!_isReady) {
|
|
106
|
-
throw new
|
|
135
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call gameOver() before screen is ready");
|
|
107
136
|
}
|
|
108
137
|
broadcasts.push({ event: "smore:game-over", data: { results } });
|
|
109
138
|
},
|
|
110
139
|
signalReady() {
|
|
111
140
|
if (_isDestroyed) {
|
|
112
|
-
throw new
|
|
141
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call signalReady() after destroy()");
|
|
113
142
|
}
|
|
114
143
|
if (!_isReady) {
|
|
115
|
-
throw new
|
|
144
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call signalReady() before screen is ready");
|
|
116
145
|
}
|
|
117
146
|
},
|
|
118
147
|
// === Event Subscription ===
|
|
119
148
|
on(event, handler) {
|
|
120
|
-
validateEventName(event);
|
|
121
149
|
const eventStr = event;
|
|
150
|
+
if (eventStr.startsWith("$")) {
|
|
151
|
+
switch (eventStr) {
|
|
152
|
+
case "$controller-join":
|
|
153
|
+
return screen.onControllerJoin(handler);
|
|
154
|
+
case "$controller-leave":
|
|
155
|
+
return screen.onControllerLeave(handler);
|
|
156
|
+
case "$controller-disconnect":
|
|
157
|
+
return screen.onControllerDisconnect(handler);
|
|
158
|
+
case "$controller-reconnect":
|
|
159
|
+
return screen.onControllerReconnect(handler);
|
|
160
|
+
case "$character-updated":
|
|
161
|
+
return screen.onCharacterUpdated(handler);
|
|
162
|
+
case "$all-ready":
|
|
163
|
+
return screen.onAllReady(handler);
|
|
164
|
+
case "$error":
|
|
165
|
+
return screen.onError(handler);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
validateEventName(event);
|
|
122
169
|
if (!listeners.has(eventStr)) {
|
|
123
170
|
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
124
171
|
}
|
|
@@ -144,6 +191,13 @@ function createMockScreen(options = {}) {
|
|
|
144
191
|
listeners.get(eventStr)?.delete(handler);
|
|
145
192
|
}
|
|
146
193
|
},
|
|
194
|
+
removeAllListeners(event) {
|
|
195
|
+
if (event) {
|
|
196
|
+
listeners.delete(event);
|
|
197
|
+
} else {
|
|
198
|
+
listeners.clear();
|
|
199
|
+
}
|
|
200
|
+
},
|
|
147
201
|
// === Utilities ===
|
|
148
202
|
getController(playerIndex) {
|
|
149
203
|
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
@@ -151,9 +205,6 @@ function createMockScreen(options = {}) {
|
|
|
151
205
|
getControllerCount() {
|
|
152
206
|
return _controllers.filter((c) => c.connected).length;
|
|
153
207
|
},
|
|
154
|
-
hasAnyConnectedControllers() {
|
|
155
|
-
return _controllers.some((c) => c.connected);
|
|
156
|
-
},
|
|
157
208
|
// === Cleanup ===
|
|
158
209
|
/**
|
|
159
210
|
* Note: destroy() clears recorded broadcast/event arrays. Call getBroadcasts() before destroy() if assertions are needed.
|
|
@@ -177,86 +228,59 @@ function createMockScreen(options = {}) {
|
|
|
177
228
|
},
|
|
178
229
|
simulateControllerJoin(info) {
|
|
179
230
|
_controllers.push(info);
|
|
180
|
-
|
|
181
|
-
onControllerJoinCallback(info.playerIndex, info);
|
|
182
|
-
}
|
|
231
|
+
_onControllerJoinCallbacks.forEach((cb) => cb(info.playerIndex, info));
|
|
183
232
|
},
|
|
184
233
|
simulateControllerLeave(playerIndex) {
|
|
185
234
|
_controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);
|
|
186
|
-
|
|
187
|
-
onControllerLeaveCallback(playerIndex);
|
|
188
|
-
}
|
|
235
|
+
_onControllerLeaveCallbacks.forEach((cb) => cb(playerIndex));
|
|
189
236
|
},
|
|
190
237
|
/**
|
|
191
238
|
* Simulate a controller network disconnect (player still in room but unreachable).
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* ```ts
|
|
195
|
-
* screen.simulateControllerDisconnect(0);
|
|
196
|
-
* expect(screen.getController(0)?.connected).toBe(false);
|
|
197
|
-
* ```
|
|
198
239
|
*/
|
|
199
240
|
simulateControllerDisconnect(playerIndex) {
|
|
200
241
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
201
242
|
if (!controller) {
|
|
202
|
-
throw new
|
|
243
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
203
244
|
}
|
|
204
245
|
_controllers = _controllers.map(
|
|
205
246
|
(c) => c.playerIndex === playerIndex ? { ...c, connected: false } : c
|
|
206
247
|
);
|
|
207
|
-
|
|
208
|
-
onControllerDisconnectCallback(playerIndex);
|
|
209
|
-
}
|
|
248
|
+
_onControllerDisconnectCallbacks.forEach((cb) => cb(playerIndex));
|
|
210
249
|
},
|
|
211
250
|
/**
|
|
212
251
|
* Simulate a controller network reconnect after disconnect.
|
|
213
|
-
*
|
|
214
|
-
* @example
|
|
215
|
-
* ```ts
|
|
216
|
-
* screen.simulateControllerDisconnect(0);
|
|
217
|
-
* screen.simulateControllerReconnect(0);
|
|
218
|
-
* expect(screen.getController(0)?.connected).toBe(true);
|
|
219
|
-
* ```
|
|
220
252
|
*/
|
|
221
253
|
simulateControllerReconnect(playerIndex) {
|
|
222
254
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
223
255
|
if (!controller) {
|
|
224
|
-
throw new
|
|
256
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
225
257
|
}
|
|
226
258
|
const reconnectedController = { ...controller, connected: true };
|
|
227
259
|
_controllers = _controllers.map(
|
|
228
260
|
(c) => c.playerIndex === playerIndex ? reconnectedController : c
|
|
229
261
|
);
|
|
230
|
-
|
|
231
|
-
onControllerReconnectCallback(playerIndex, reconnectedController);
|
|
232
|
-
}
|
|
262
|
+
_onControllerReconnectCallbacks.forEach((cb) => cb(playerIndex, reconnectedController));
|
|
233
263
|
},
|
|
234
264
|
simulateCharacterUpdate(playerIndex, appearance) {
|
|
235
265
|
const controller = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
236
266
|
if (!controller) {
|
|
237
|
-
throw new
|
|
267
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
238
268
|
}
|
|
239
269
|
_controllers = _controllers.map(
|
|
240
270
|
(c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
|
|
241
271
|
);
|
|
242
|
-
|
|
243
|
-
onCharacterUpdatedCallback(playerIndex, appearance);
|
|
244
|
-
}
|
|
245
|
-
},
|
|
246
|
-
simulateRateLimited(event) {
|
|
247
|
-
if (onRateLimitedCallback) {
|
|
248
|
-
onRateLimitedCallback(event);
|
|
249
|
-
}
|
|
272
|
+
_onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
|
|
250
273
|
},
|
|
251
274
|
simulateAllReady() {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
275
|
+
_allReadyFired = true;
|
|
276
|
+
_onAllReadyCallbacks.forEach((cb) => cb());
|
|
255
277
|
},
|
|
256
278
|
simulateError(error) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
279
|
+
_onErrorCallbacks.forEach((cb) => cb(error));
|
|
280
|
+
},
|
|
281
|
+
simulateConnectionChange(connected) {
|
|
282
|
+
_isConnected = connected;
|
|
283
|
+
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
260
284
|
},
|
|
261
285
|
getBroadcasts() {
|
|
262
286
|
return [...broadcasts];
|
|
@@ -270,6 +294,8 @@ function createMockScreen(options = {}) {
|
|
|
270
294
|
},
|
|
271
295
|
triggerReady() {
|
|
272
296
|
_isReady = true;
|
|
297
|
+
_isConnected = true;
|
|
298
|
+
_readyResolve();
|
|
273
299
|
}
|
|
274
300
|
};
|
|
275
301
|
if (autoReady) {
|
|
@@ -280,42 +306,33 @@ function createMockScreen(options = {}) {
|
|
|
280
306
|
function createMockController(options = {}) {
|
|
281
307
|
const {
|
|
282
308
|
roomCode = "TEST",
|
|
283
|
-
|
|
284
|
-
autoReady = true
|
|
285
|
-
onControllerJoin: onJoinCb,
|
|
286
|
-
onControllerLeave: onLeaveCb,
|
|
287
|
-
onControllerDisconnect: onDisconnectCb,
|
|
288
|
-
onControllerReconnect: onReconnectCb,
|
|
289
|
-
onCharacterUpdated: onCharacterUpdatedCb,
|
|
290
|
-
onRateLimited: onRateLimitedCb,
|
|
291
|
-
onReady: onReadyCb,
|
|
292
|
-
onError: onErrorCb
|
|
309
|
+
myPlayerIndex = 0,
|
|
310
|
+
autoReady = true
|
|
293
311
|
} = options;
|
|
294
312
|
let _isReady = false;
|
|
313
|
+
let _isConnected = false;
|
|
295
314
|
let _isDestroyed = false;
|
|
296
315
|
let _controllers = options.controllers ?? [];
|
|
297
316
|
const listeners = /* @__PURE__ */ new Map();
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
onReadyCallback = onReadyCb;
|
|
313
|
-
onErrorCallback = onErrorCb;
|
|
317
|
+
const _onAllReadyCallbacks = /* @__PURE__ */ new Set();
|
|
318
|
+
const _onControllerJoinCallbacks = /* @__PURE__ */ new Set();
|
|
319
|
+
const _onControllerLeaveCallbacks = /* @__PURE__ */ new Set();
|
|
320
|
+
const _onControllerDisconnectCallbacks = /* @__PURE__ */ new Set();
|
|
321
|
+
const _onControllerReconnectCallbacks = /* @__PURE__ */ new Set();
|
|
322
|
+
const _onCharacterUpdatedCallbacks = /* @__PURE__ */ new Set();
|
|
323
|
+
const _onErrorCallbacks = /* @__PURE__ */ new Set();
|
|
324
|
+
const _onGameOverCallbacks = /* @__PURE__ */ new Set();
|
|
325
|
+
const _onConnectionChangeCallbacks = /* @__PURE__ */ new Set();
|
|
326
|
+
let _allReadyFired = false;
|
|
327
|
+
let _readyResolve;
|
|
328
|
+
const _readyPromise = new Promise((resolve) => {
|
|
329
|
+
_readyResolve = resolve;
|
|
330
|
+
});
|
|
314
331
|
const sentEvents = [];
|
|
315
332
|
const controller = {
|
|
316
333
|
// === Properties ===
|
|
317
|
-
get
|
|
318
|
-
return
|
|
334
|
+
get myPlayerIndex() {
|
|
335
|
+
return myPlayerIndex;
|
|
319
336
|
},
|
|
320
337
|
get roomCode() {
|
|
321
338
|
return roomCode;
|
|
@@ -326,39 +343,122 @@ function createMockController(options = {}) {
|
|
|
326
343
|
get isDestroyed() {
|
|
327
344
|
return _isDestroyed;
|
|
328
345
|
},
|
|
346
|
+
get isConnected() {
|
|
347
|
+
return _isConnected;
|
|
348
|
+
},
|
|
349
|
+
get protocolVersion() {
|
|
350
|
+
return 1;
|
|
351
|
+
},
|
|
329
352
|
get controllers() {
|
|
330
353
|
return [..._controllers];
|
|
331
354
|
},
|
|
355
|
+
get ready() {
|
|
356
|
+
return _readyPromise;
|
|
357
|
+
},
|
|
358
|
+
// === Lifecycle Methods ===
|
|
359
|
+
onAllReady(callback) {
|
|
360
|
+
if (_allReadyFired) {
|
|
361
|
+
callback();
|
|
362
|
+
}
|
|
363
|
+
_onAllReadyCallbacks.add(callback);
|
|
364
|
+
return () => {
|
|
365
|
+
_onAllReadyCallbacks.delete(callback);
|
|
366
|
+
};
|
|
367
|
+
},
|
|
368
|
+
onControllerJoin(callback) {
|
|
369
|
+
_onControllerJoinCallbacks.add(callback);
|
|
370
|
+
return () => {
|
|
371
|
+
_onControllerJoinCallbacks.delete(callback);
|
|
372
|
+
};
|
|
373
|
+
},
|
|
374
|
+
onControllerLeave(callback) {
|
|
375
|
+
_onControllerLeaveCallbacks.add(callback);
|
|
376
|
+
return () => {
|
|
377
|
+
_onControllerLeaveCallbacks.delete(callback);
|
|
378
|
+
};
|
|
379
|
+
},
|
|
380
|
+
onControllerDisconnect(callback) {
|
|
381
|
+
_onControllerDisconnectCallbacks.add(callback);
|
|
382
|
+
return () => {
|
|
383
|
+
_onControllerDisconnectCallbacks.delete(callback);
|
|
384
|
+
};
|
|
385
|
+
},
|
|
386
|
+
onControllerReconnect(callback) {
|
|
387
|
+
_onControllerReconnectCallbacks.add(callback);
|
|
388
|
+
return () => {
|
|
389
|
+
_onControllerReconnectCallbacks.delete(callback);
|
|
390
|
+
};
|
|
391
|
+
},
|
|
392
|
+
onCharacterUpdated(callback) {
|
|
393
|
+
_onCharacterUpdatedCallbacks.add(callback);
|
|
394
|
+
return () => {
|
|
395
|
+
_onCharacterUpdatedCallbacks.delete(callback);
|
|
396
|
+
};
|
|
397
|
+
},
|
|
398
|
+
onError(callback) {
|
|
399
|
+
_onErrorCallbacks.add(callback);
|
|
400
|
+
return () => {
|
|
401
|
+
_onErrorCallbacks.delete(callback);
|
|
402
|
+
};
|
|
403
|
+
},
|
|
404
|
+
onConnectionChange(callback) {
|
|
405
|
+
_onConnectionChangeCallbacks.add(callback);
|
|
406
|
+
return () => {
|
|
407
|
+
_onConnectionChangeCallbacks.delete(callback);
|
|
408
|
+
};
|
|
409
|
+
},
|
|
410
|
+
onGameOver(callback) {
|
|
411
|
+
_onGameOverCallbacks.add(callback);
|
|
412
|
+
return () => {
|
|
413
|
+
_onGameOverCallbacks.delete(callback);
|
|
414
|
+
};
|
|
415
|
+
},
|
|
332
416
|
getControllerCount() {
|
|
333
417
|
return _controllers.filter((c) => c.connected).length;
|
|
334
418
|
},
|
|
419
|
+
getController(playerIndex) {
|
|
420
|
+
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
421
|
+
},
|
|
335
422
|
// === Communication Methods ===
|
|
336
423
|
send(event, data) {
|
|
337
424
|
if (_isDestroyed) {
|
|
338
|
-
throw new
|
|
339
|
-
}
|
|
340
|
-
validateEventName(event);
|
|
341
|
-
sentEvents.push({ event, data });
|
|
342
|
-
},
|
|
343
|
-
sendRaw(event, data) {
|
|
344
|
-
if (_isDestroyed) {
|
|
345
|
-
throw new Error("Cannot send: controller is destroyed");
|
|
425
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call send() after destroy()");
|
|
346
426
|
}
|
|
347
427
|
validateEventName(event);
|
|
348
428
|
sentEvents.push({ event, data });
|
|
349
429
|
},
|
|
350
430
|
signalReady() {
|
|
351
431
|
if (_isDestroyed) {
|
|
352
|
-
throw new
|
|
432
|
+
throw new SmoreSDKError("DESTROYED", "Cannot call signalReady() after destroy()");
|
|
353
433
|
}
|
|
354
434
|
if (!_isReady) {
|
|
355
|
-
throw new
|
|
435
|
+
throw new SmoreSDKError("NOT_READY", "Cannot call signalReady() before controller is ready");
|
|
356
436
|
}
|
|
357
437
|
},
|
|
358
438
|
// === Event Subscription ===
|
|
359
439
|
on(event, handler) {
|
|
360
|
-
validateEventName(event);
|
|
361
440
|
const eventStr = event;
|
|
441
|
+
if (eventStr.startsWith("$")) {
|
|
442
|
+
switch (eventStr) {
|
|
443
|
+
case "$controller-join":
|
|
444
|
+
return controller.onControllerJoin(handler);
|
|
445
|
+
case "$controller-leave":
|
|
446
|
+
return controller.onControllerLeave(handler);
|
|
447
|
+
case "$controller-disconnect":
|
|
448
|
+
return controller.onControllerDisconnect(handler);
|
|
449
|
+
case "$controller-reconnect":
|
|
450
|
+
return controller.onControllerReconnect(handler);
|
|
451
|
+
case "$character-updated":
|
|
452
|
+
return controller.onCharacterUpdated(handler);
|
|
453
|
+
case "$all-ready":
|
|
454
|
+
return controller.onAllReady(handler);
|
|
455
|
+
case "$error":
|
|
456
|
+
return controller.onError(handler);
|
|
457
|
+
case "$game-over":
|
|
458
|
+
return controller.onGameOver(handler);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
validateEventName(event);
|
|
362
462
|
if (!listeners.has(eventStr)) {
|
|
363
463
|
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
364
464
|
}
|
|
@@ -384,6 +484,13 @@ function createMockController(options = {}) {
|
|
|
384
484
|
listeners.get(eventStr)?.delete(handler);
|
|
385
485
|
}
|
|
386
486
|
},
|
|
487
|
+
removeAllListeners(event) {
|
|
488
|
+
if (event) {
|
|
489
|
+
listeners.delete(event);
|
|
490
|
+
} else {
|
|
491
|
+
listeners.clear();
|
|
492
|
+
}
|
|
493
|
+
},
|
|
387
494
|
// === Cleanup ===
|
|
388
495
|
destroy() {
|
|
389
496
|
_isDestroyed = true;
|
|
@@ -409,102 +516,67 @@ function createMockController(options = {}) {
|
|
|
409
516
|
},
|
|
410
517
|
triggerReady() {
|
|
411
518
|
_isReady = true;
|
|
519
|
+
_isConnected = true;
|
|
520
|
+
_readyResolve();
|
|
412
521
|
},
|
|
413
522
|
/**
|
|
414
523
|
* Simulate a new player joining the room.
|
|
415
524
|
* Stores full ControllerInfo (nickname, appearance, etc.) for later retrieval.
|
|
416
|
-
*
|
|
417
|
-
* @example
|
|
418
|
-
* ```ts
|
|
419
|
-
* controller.simulatePlayerJoin(2, { playerIndex: 2, nickname: 'Alice', connected: true });
|
|
420
|
-
* expect(controller.getControllerCount()).toBe(3);
|
|
421
|
-
* expect(controller.controllers.find(c => c.playerIndex === 2)?.nickname).toBe('Alice');
|
|
422
|
-
* ```
|
|
423
525
|
*/
|
|
424
526
|
simulatePlayerJoin(playerIndex, info) {
|
|
425
527
|
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
426
528
|
_controllers = [..._controllers, { ...info, connected: info.connected ?? true }];
|
|
427
529
|
}
|
|
428
|
-
|
|
429
|
-
onControllerJoinCallback(playerIndex, info);
|
|
430
|
-
}
|
|
530
|
+
_onControllerJoinCallbacks.forEach((cb) => cb(playerIndex, info));
|
|
431
531
|
},
|
|
432
532
|
/**
|
|
433
533
|
* Simulate a player leaving the room (fully removed).
|
|
434
|
-
*
|
|
435
|
-
* @example
|
|
436
|
-
* ```ts
|
|
437
|
-
* controller.simulatePlayerLeave(1);
|
|
438
|
-
* expect(controller.controllers.some(c => c.playerIndex === 1)).toBe(false);
|
|
439
|
-
* ```
|
|
440
534
|
*/
|
|
441
535
|
simulatePlayerLeave(playerIndex) {
|
|
442
536
|
_controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);
|
|
443
|
-
|
|
444
|
-
onControllerLeaveCallback(playerIndex);
|
|
445
|
-
}
|
|
537
|
+
_onControllerLeaveCallbacks.forEach((cb) => cb(playerIndex));
|
|
446
538
|
},
|
|
447
539
|
/**
|
|
448
540
|
* Simulate a player network disconnect (player still in room but unreachable).
|
|
449
|
-
*
|
|
450
|
-
* @example
|
|
451
|
-
* ```ts
|
|
452
|
-
* controller.simulatePlayerDisconnect(1);
|
|
453
|
-
* expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(false);
|
|
454
|
-
* ```
|
|
455
541
|
*/
|
|
456
542
|
simulatePlayerDisconnect(playerIndex) {
|
|
457
543
|
_controllers = _controllers.map(
|
|
458
544
|
(c) => c.playerIndex === playerIndex ? { ...c, connected: false } : c
|
|
459
545
|
);
|
|
460
|
-
|
|
461
|
-
onControllerDisconnectCallback(playerIndex);
|
|
462
|
-
}
|
|
546
|
+
_onControllerDisconnectCallbacks.forEach((cb) => cb(playerIndex));
|
|
463
547
|
},
|
|
464
548
|
/**
|
|
465
549
|
* Simulate a player network reconnect after disconnect.
|
|
466
|
-
*
|
|
467
|
-
* @example
|
|
468
|
-
* ```ts
|
|
469
|
-
* controller.simulatePlayerDisconnect(1);
|
|
470
|
-
* controller.simulatePlayerReconnect(1, { playerIndex: 1, nickname: 'Bob', connected: true });
|
|
471
|
-
* expect(controller.controllers.find(c => c.playerIndex === 1)?.connected).toBe(true);
|
|
472
|
-
* ```
|
|
473
550
|
*/
|
|
474
551
|
simulatePlayerReconnect(playerIndex, info) {
|
|
475
552
|
_controllers = _controllers.map(
|
|
476
553
|
(c) => c.playerIndex === playerIndex ? { ...info, connected: true } : c
|
|
477
554
|
);
|
|
478
|
-
|
|
479
|
-
onControllerReconnectCallback(playerIndex, info);
|
|
480
|
-
}
|
|
555
|
+
_onControllerReconnectCallbacks.forEach((cb) => cb(playerIndex, info));
|
|
481
556
|
},
|
|
482
557
|
simulateCharacterUpdate(playerIndex, appearance) {
|
|
483
|
-
const
|
|
484
|
-
if (!
|
|
485
|
-
throw new
|
|
558
|
+
const ctrl = _controllers.find((c) => c.playerIndex === playerIndex);
|
|
559
|
+
if (!ctrl) {
|
|
560
|
+
throw new SmoreSDKError("INVALID_PLAYER", `Controller ${playerIndex} not found`);
|
|
486
561
|
}
|
|
487
562
|
_controllers = _controllers.map(
|
|
488
563
|
(c) => c.playerIndex === playerIndex ? { ...c, appearance } : c
|
|
489
564
|
);
|
|
490
|
-
|
|
491
|
-
onCharacterUpdatedCallback(playerIndex, appearance);
|
|
492
|
-
}
|
|
493
|
-
},
|
|
494
|
-
simulateRateLimited(event) {
|
|
495
|
-
if (onRateLimitedCallback) {
|
|
496
|
-
onRateLimitedCallback(event);
|
|
497
|
-
}
|
|
565
|
+
_onCharacterUpdatedCallbacks.forEach((cb) => cb(playerIndex, appearance));
|
|
498
566
|
},
|
|
499
567
|
simulateAllReady() {
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
}
|
|
568
|
+
_allReadyFired = true;
|
|
569
|
+
_onAllReadyCallbacks.forEach((cb) => cb());
|
|
503
570
|
},
|
|
504
571
|
simulateError(error) {
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
572
|
+
_onErrorCallbacks.forEach((cb) => cb(error));
|
|
573
|
+
},
|
|
574
|
+
simulateGameOver(results) {
|
|
575
|
+
_onGameOverCallbacks.forEach((cb) => cb(results));
|
|
576
|
+
},
|
|
577
|
+
simulateConnectionChange(connected) {
|
|
578
|
+
_isConnected = connected;
|
|
579
|
+
_onConnectionChangeCallbacks.forEach((cb) => cb(connected));
|
|
508
580
|
}
|
|
509
581
|
};
|
|
510
582
|
if (autoReady) {
|