@smoregg/sdk 0.4.1 → 0.6.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 +199 -0
- package/dist/cjs/SmoreHost.cjs +306 -0
- package/dist/cjs/SmoreHost.cjs.map +1 -0
- package/dist/cjs/SmorePlayer.cjs +229 -0
- package/dist/cjs/SmorePlayer.cjs.map +1 -0
- package/dist/cjs/components/IframeGameBridge.cjs +115 -0
- package/dist/cjs/components/IframeGameBridge.cjs.map +1 -0
- package/dist/cjs/context/RoomProvider.cjs +3 -3
- package/dist/cjs/context/RoomProvider.cjs.map +1 -1
- package/dist/cjs/controller.cjs +379 -0
- package/dist/cjs/controller.cjs.map +1 -0
- package/dist/cjs/hooks/useGameHost.cjs +86 -13
- package/dist/cjs/hooks/useGameHost.cjs.map +1 -1
- package/dist/cjs/hooks/useGamePlayer.cjs +60 -4
- package/dist/cjs/hooks/useGamePlayer.cjs.map +1 -1
- package/dist/cjs/iframe/index.cjs +50 -316
- package/dist/cjs/iframe/index.cjs.map +1 -1
- package/dist/cjs/index.cjs +8 -22
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/screen.cjs +526 -0
- package/dist/cjs/screen.cjs.map +1 -0
- package/dist/cjs/testing.cjs +257 -0
- package/dist/cjs/testing.cjs.map +1 -0
- package/dist/cjs/transport/protocol.cjs.map +1 -1
- package/dist/cjs/utils/connectionMonitor.cjs +77 -0
- package/dist/cjs/utils/connectionMonitor.cjs.map +1 -0
- package/dist/cjs/utils/preloadAssets.cjs +66 -0
- package/dist/cjs/utils/preloadAssets.cjs.map +1 -0
- package/dist/cjs/utils/serverTime.cjs +43 -0
- package/dist/cjs/utils/serverTime.cjs.map +1 -0
- package/dist/esm/SmoreHost.js +304 -0
- package/dist/esm/SmoreHost.js.map +1 -0
- package/dist/esm/SmorePlayer.js +227 -0
- package/dist/esm/SmorePlayer.js.map +1 -0
- package/dist/esm/components/IframeGameBridge.js +113 -0
- package/dist/esm/components/IframeGameBridge.js.map +1 -0
- package/dist/esm/context/RoomProvider.js +3 -3
- package/dist/esm/context/RoomProvider.js.map +1 -1
- package/dist/esm/controller.js +376 -0
- package/dist/esm/controller.js.map +1 -0
- package/dist/esm/hooks/useGameHost.js +87 -14
- package/dist/esm/hooks/useGameHost.js.map +1 -1
- package/dist/esm/hooks/useGamePlayer.js +61 -5
- package/dist/esm/hooks/useGamePlayer.js.map +1 -1
- package/dist/esm/iframe/index.js +51 -314
- package/dist/esm/iframe/index.js.map +1 -1
- package/dist/esm/index.js +3 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/screen.js +523 -0
- package/dist/esm/screen.js.map +1 -0
- package/dist/esm/testing.js +254 -0
- package/dist/esm/testing.js.map +1 -0
- package/dist/esm/transport/protocol.js.map +1 -1
- package/dist/esm/utils/connectionMonitor.js +75 -0
- package/dist/esm/utils/connectionMonitor.js.map +1 -0
- package/dist/esm/utils/preloadAssets.js +63 -0
- package/dist/esm/utils/preloadAssets.js.map +1 -0
- package/dist/esm/utils/serverTime.js +41 -0
- package/dist/esm/utils/serverTime.js.map +1 -0
- package/dist/types/SmoreHost.d.ts +187 -0
- package/dist/types/SmoreHost.d.ts.map +1 -0
- package/dist/types/SmorePlayer.d.ts +146 -0
- package/dist/types/SmorePlayer.d.ts.map +1 -0
- package/dist/types/components/IframeGameBridge.d.ts +2 -2
- package/dist/types/components/IframeGameBridge.d.ts.map +1 -1
- package/dist/types/components/index.d.ts +2 -4
- package/dist/types/components/index.d.ts.map +1 -1
- package/dist/types/context/RoomProvider.d.ts +3 -3
- package/dist/types/context/RoomProvider.d.ts.map +1 -1
- package/dist/types/controller.d.ts +78 -0
- package/dist/types/controller.d.ts.map +1 -0
- package/dist/types/hooks/useGameHost.d.ts +33 -7
- package/dist/types/hooks/useGameHost.d.ts.map +1 -1
- package/dist/types/hooks/useGamePlayer.d.ts +29 -3
- package/dist/types/hooks/useGamePlayer.d.ts.map +1 -1
- package/dist/types/iframe/index.d.ts +10 -10
- package/dist/types/iframe/index.d.ts.map +1 -1
- package/dist/types/iframe/vanilla.d.ts +12 -4
- package/dist/types/iframe/vanilla.d.ts.map +1 -1
- package/dist/types/index.d.ts +36 -21
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/screen.d.ts +79 -0
- package/dist/types/screen.d.ts.map +1 -0
- package/dist/types/testing.d.ts +61 -0
- package/dist/types/testing.d.ts.map +1 -0
- package/dist/types/transport/protocol.d.ts +2 -5
- package/dist/types/transport/protocol.d.ts.map +1 -1
- package/dist/types/types.d.ts +869 -4
- package/dist/types/types.d.ts.map +1 -1
- package/dist/types/utils/connectionMonitor.d.ts +57 -0
- package/dist/types/utils/connectionMonitor.d.ts.map +1 -0
- package/dist/types/utils/index.d.ts +7 -0
- package/dist/types/utils/index.d.ts.map +1 -0
- package/dist/types/utils/preloadAssets.d.ts +29 -0
- package/dist/types/utils/preloadAssets.d.ts.map +1 -0
- package/dist/types/utils/serverTime.d.ts +28 -0
- package/dist/types/utils/serverTime.d.ts.map +1 -0
- package/dist/umd/smore-sdk-iframe.umd.js +54 -317
- package/dist/umd/smore-sdk-iframe.umd.js.map +1 -1
- package/dist/umd/smore-sdk-iframe.umd.min.js +1 -1
- package/dist/umd/smore-sdk-iframe.umd.min.js.map +1 -1
- package/dist/umd/smore-sdk-vanilla.umd.js +1166 -117
- package/dist/umd/smore-sdk-vanilla.umd.js.map +1 -1
- package/dist/umd/smore-sdk-vanilla.umd.min.js +1 -1
- package/dist/umd/smore-sdk-vanilla.umd.min.js.map +1 -1
- package/dist/umd/smore-sdk.umd.js +1139 -602
- 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 +1 -26
|
@@ -1,382 +1,8 @@
|
|
|
1
1
|
(function (global, factory) {
|
|
2
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports
|
|
3
|
-
typeof define === 'function' && define.amd ? define(['exports'
|
|
4
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SmoreSDK = {}
|
|
5
|
-
})(this, (function (exports
|
|
6
|
-
|
|
7
|
-
function styleInject(css, ref) {
|
|
8
|
-
if ( ref === void 0 ) ref = {};
|
|
9
|
-
var insertAt = ref.insertAt;
|
|
10
|
-
|
|
11
|
-
if (!css || typeof document === 'undefined') { return; }
|
|
12
|
-
|
|
13
|
-
var head = document.head || document.getElementsByTagName('head')[0];
|
|
14
|
-
var style = document.createElement('style');
|
|
15
|
-
style.type = 'text/css';
|
|
16
|
-
|
|
17
|
-
if (insertAt === 'top') {
|
|
18
|
-
if (head.firstChild) {
|
|
19
|
-
head.insertBefore(style, head.firstChild);
|
|
20
|
-
} else {
|
|
21
|
-
head.appendChild(style);
|
|
22
|
-
}
|
|
23
|
-
} else {
|
|
24
|
-
head.appendChild(style);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (style.styleSheet) {
|
|
28
|
-
style.styleSheet.cssText = css;
|
|
29
|
-
} else {
|
|
30
|
-
style.appendChild(document.createTextNode(css));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
var css_248z$3 = ".TapButton-module_tapButton__dqJr9{-webkit-tap-highlight-color:transparent;align-items:center;background:var(--color-primary,#6366f1);border:none;border-radius:16px;color:#fff;cursor:pointer;display:flex;font-size:18px;font-weight:700;justify-content:center;min-height:80px;min-width:80px;padding:16px 24px;touch-action:manipulation;transition:transform .1s ease,background .1s ease;user-select:none;-webkit-user-select:none}.TapButton-module_pressed__Z2nfR,.TapButton-module_tapButton__dqJr9:active{background:var(--color-primary-dark,#4f46e5);transform:scale(.95)}.TapButton-module_disabled__3bR6q{cursor:not-allowed;opacity:.5}.TapButton-module_disabled__3bR6q.TapButton-module_pressed__Z2nfR,.TapButton-module_disabled__3bR6q:active{background:var(--color-primary,#6366f1);transform:none}";
|
|
35
|
-
var styles$3 = {"tapButton":"TapButton-module_tapButton__dqJr9","pressed":"TapButton-module_pressed__Z2nfR","disabled":"TapButton-module_disabled__3bR6q"};
|
|
36
|
-
styleInject(css_248z$3);
|
|
37
|
-
|
|
38
|
-
function TapButton({
|
|
39
|
-
onTap,
|
|
40
|
-
children,
|
|
41
|
-
className,
|
|
42
|
-
disabled = false
|
|
43
|
-
}) {
|
|
44
|
-
const isPressed = react.useRef(false);
|
|
45
|
-
const buttonRef = react.useRef(null);
|
|
46
|
-
const handleTouchStart = react.useCallback((e) => {
|
|
47
|
-
if (disabled) return;
|
|
48
|
-
e.preventDefault();
|
|
49
|
-
isPressed.current = true;
|
|
50
|
-
buttonRef.current?.classList.add(styles$3.pressed);
|
|
51
|
-
if (navigator.vibrate) {
|
|
52
|
-
navigator.vibrate(10);
|
|
53
|
-
}
|
|
54
|
-
onTap();
|
|
55
|
-
}, [onTap, disabled]);
|
|
56
|
-
const handleTouchEnd = react.useCallback((e) => {
|
|
57
|
-
e.preventDefault();
|
|
58
|
-
isPressed.current = false;
|
|
59
|
-
buttonRef.current?.classList.remove(styles$3.pressed);
|
|
60
|
-
}, []);
|
|
61
|
-
const handleMouseDown = react.useCallback(() => {
|
|
62
|
-
if (disabled) return;
|
|
63
|
-
isPressed.current = true;
|
|
64
|
-
buttonRef.current?.classList.add(styles$3.pressed);
|
|
65
|
-
onTap();
|
|
66
|
-
}, [onTap, disabled]);
|
|
67
|
-
const handleMouseUp = react.useCallback(() => {
|
|
68
|
-
isPressed.current = false;
|
|
69
|
-
buttonRef.current?.classList.remove(styles$3.pressed);
|
|
70
|
-
}, []);
|
|
71
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
72
|
-
"button",
|
|
73
|
-
{
|
|
74
|
-
ref: buttonRef,
|
|
75
|
-
className: `${styles$3.tapButton} ${className || ""} ${disabled ? styles$3.disabled : ""}`,
|
|
76
|
-
onTouchStart: handleTouchStart,
|
|
77
|
-
onTouchEnd: handleTouchEnd,
|
|
78
|
-
onTouchCancel: handleTouchEnd,
|
|
79
|
-
onMouseDown: handleMouseDown,
|
|
80
|
-
onMouseUp: handleMouseUp,
|
|
81
|
-
onMouseLeave: handleMouseUp,
|
|
82
|
-
disabled,
|
|
83
|
-
children
|
|
84
|
-
}
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
var css_248z$2 = ".HoldButton-module_holdButton__tu4mi{-webkit-tap-highlight-color:transparent;align-items:center;background:var(--color-secondary,#f59e0b);border:none;border-radius:16px;color:#fff;cursor:pointer;display:flex;font-size:18px;font-weight:700;justify-content:center;min-height:80px;min-width:80px;padding:16px 24px;touch-action:manipulation;transition:transform .1s ease,background .1s ease;user-select:none;-webkit-user-select:none}.HoldButton-module_holdButton__tu4mi:active,.HoldButton-module_pressed__JWIRG{background:var(--color-secondary-dark,#d97706);transform:scale(.95)}.HoldButton-module_disabled__4QwwT{cursor:not-allowed;opacity:.5}.HoldButton-module_disabled__4QwwT.HoldButton-module_pressed__JWIRG,.HoldButton-module_disabled__4QwwT:active{background:var(--color-secondary,#f59e0b);transform:none}";
|
|
89
|
-
var styles$2 = {"holdButton":"HoldButton-module_holdButton__tu4mi","pressed":"HoldButton-module_pressed__JWIRG","disabled":"HoldButton-module_disabled__4QwwT"};
|
|
90
|
-
styleInject(css_248z$2);
|
|
91
|
-
|
|
92
|
-
function HoldButton({
|
|
93
|
-
onHoldStart,
|
|
94
|
-
onHoldEnd,
|
|
95
|
-
children,
|
|
96
|
-
className,
|
|
97
|
-
disabled = false
|
|
98
|
-
}) {
|
|
99
|
-
const isHolding = react.useRef(false);
|
|
100
|
-
const buttonRef = react.useRef(null);
|
|
101
|
-
const startHold = react.useCallback(() => {
|
|
102
|
-
if (disabled || isHolding.current) return;
|
|
103
|
-
isHolding.current = true;
|
|
104
|
-
buttonRef.current?.classList.add(styles$2.pressed);
|
|
105
|
-
if (navigator.vibrate) {
|
|
106
|
-
navigator.vibrate(10);
|
|
107
|
-
}
|
|
108
|
-
onHoldStart();
|
|
109
|
-
}, [onHoldStart, disabled]);
|
|
110
|
-
const endHold = react.useCallback(() => {
|
|
111
|
-
if (!isHolding.current) return;
|
|
112
|
-
isHolding.current = false;
|
|
113
|
-
buttonRef.current?.classList.remove(styles$2.pressed);
|
|
114
|
-
onHoldEnd();
|
|
115
|
-
}, [onHoldEnd]);
|
|
116
|
-
const handleTouchStart = react.useCallback((e) => {
|
|
117
|
-
e.preventDefault();
|
|
118
|
-
startHold();
|
|
119
|
-
}, [startHold]);
|
|
120
|
-
const handleTouchEnd = react.useCallback((e) => {
|
|
121
|
-
e.preventDefault();
|
|
122
|
-
endHold();
|
|
123
|
-
}, [endHold]);
|
|
124
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
125
|
-
"button",
|
|
126
|
-
{
|
|
127
|
-
ref: buttonRef,
|
|
128
|
-
className: `${styles$2.holdButton} ${className || ""} ${disabled ? styles$2.disabled : ""}`,
|
|
129
|
-
onTouchStart: handleTouchStart,
|
|
130
|
-
onTouchEnd: handleTouchEnd,
|
|
131
|
-
onTouchCancel: handleTouchEnd,
|
|
132
|
-
onMouseDown: startHold,
|
|
133
|
-
onMouseUp: endHold,
|
|
134
|
-
onMouseLeave: endHold,
|
|
135
|
-
disabled,
|
|
136
|
-
children
|
|
137
|
-
}
|
|
138
|
-
);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
var css_248z$1 = ".DirectionPad-module_padContainer__iL-rh{align-items:center;display:flex;flex-direction:column;gap:8px;user-select:none;-webkit-user-select:none}.DirectionPad-module_horizontal__kI4j7{flex-direction:row;gap:16px}.DirectionPad-module_vertical__b8Xec{flex-direction:column;gap:16px}.DirectionPad-module_row__mzuUr{display:flex;gap:8px}.DirectionPad-module_dirButton__QCCHz{-webkit-tap-highlight-color:transparent;align-items:center;background:var(--color-surface,#374151);border:none;border-radius:12px;color:#fff;cursor:pointer;display:flex;font-size:24px;height:70px;justify-content:center;touch-action:manipulation;transition:transform .1s ease,background .1s ease;width:70px}.DirectionPad-module_dirButton__QCCHz:active{background:var(--color-primary,#6366f1);transform:scale(.9)}.DirectionPad-module_horizontal__kI4j7 .DirectionPad-module_dirButton__QCCHz,.DirectionPad-module_vertical__b8Xec .DirectionPad-module_dirButton__QCCHz{font-size:32px;height:100px;width:100px}";
|
|
142
|
-
var styles$1 = {"padContainer":"DirectionPad-module_padContainer__iL-rh","horizontal":"DirectionPad-module_horizontal__kI4j7","vertical":"DirectionPad-module_vertical__b8Xec","row":"DirectionPad-module_row__mzuUr","dirButton":"DirectionPad-module_dirButton__QCCHz"};
|
|
143
|
-
styleInject(css_248z$1);
|
|
144
|
-
|
|
145
|
-
function DirectionPad({
|
|
146
|
-
onDirection,
|
|
147
|
-
leftRightOnly = false,
|
|
148
|
-
upDownOnly = false,
|
|
149
|
-
className
|
|
150
|
-
}) {
|
|
151
|
-
const pressedRef = react.useRef(/* @__PURE__ */ new Set());
|
|
152
|
-
const handlePress = react.useCallback((direction) => {
|
|
153
|
-
if (pressedRef.current.has(direction)) return;
|
|
154
|
-
pressedRef.current.add(direction);
|
|
155
|
-
if (navigator.vibrate) {
|
|
156
|
-
navigator.vibrate(10);
|
|
157
|
-
}
|
|
158
|
-
onDirection(direction);
|
|
159
|
-
}, [onDirection]);
|
|
160
|
-
const handleRelease = react.useCallback((direction) => {
|
|
161
|
-
pressedRef.current.delete(direction);
|
|
162
|
-
}, []);
|
|
163
|
-
const createButton = (direction, label) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
164
|
-
"button",
|
|
165
|
-
{
|
|
166
|
-
className: `${styles$1.dirButton} ${styles$1[direction]}`,
|
|
167
|
-
onTouchStart: (e) => {
|
|
168
|
-
e.preventDefault();
|
|
169
|
-
handlePress(direction);
|
|
170
|
-
},
|
|
171
|
-
onTouchEnd: (e) => {
|
|
172
|
-
e.preventDefault();
|
|
173
|
-
handleRelease(direction);
|
|
174
|
-
},
|
|
175
|
-
onTouchCancel: () => handleRelease(direction),
|
|
176
|
-
onMouseDown: () => handlePress(direction),
|
|
177
|
-
onMouseUp: () => handleRelease(direction),
|
|
178
|
-
onMouseLeave: () => handleRelease(direction),
|
|
179
|
-
children: label
|
|
180
|
-
},
|
|
181
|
-
direction
|
|
182
|
-
);
|
|
183
|
-
if (leftRightOnly) {
|
|
184
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles$1.padContainer} ${styles$1.horizontal} ${className || ""}`, children: [
|
|
185
|
-
createButton("left", "\u25C0"),
|
|
186
|
-
createButton("right", "\u25B6")
|
|
187
|
-
] });
|
|
188
|
-
}
|
|
189
|
-
if (upDownOnly) {
|
|
190
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles$1.padContainer} ${styles$1.vertical} ${className || ""}`, children: [
|
|
191
|
-
createButton("up", "\u25B2"),
|
|
192
|
-
createButton("down", "\u25BC")
|
|
193
|
-
] });
|
|
194
|
-
}
|
|
195
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${styles$1.padContainer} ${styles$1.full} ${className || ""}`, children: [
|
|
196
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$1.row, children: createButton("up", "\u25B2") }),
|
|
197
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles$1.row, children: [
|
|
198
|
-
createButton("left", "\u25C0"),
|
|
199
|
-
createButton("right", "\u25B6")
|
|
200
|
-
] }),
|
|
201
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles$1.row, children: createButton("down", "\u25BC") })
|
|
202
|
-
] });
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
var css_248z = ".SwipeArea-module_swipeArea__yob7L{height:100%;touch-action:none;user-select:none;-webkit-user-select:none;width:100%}";
|
|
206
|
-
var styles = {"swipeArea":"SwipeArea-module_swipeArea__yob7L"};
|
|
207
|
-
styleInject(css_248z);
|
|
208
|
-
|
|
209
|
-
function SwipeArea({
|
|
210
|
-
onSwipe,
|
|
211
|
-
threshold = 50,
|
|
212
|
-
children,
|
|
213
|
-
className
|
|
214
|
-
}) {
|
|
215
|
-
const touchStart = react.useRef(null);
|
|
216
|
-
const handleTouchStart = react.useCallback((e) => {
|
|
217
|
-
const touch = e.touches[0];
|
|
218
|
-
if (!touch) return;
|
|
219
|
-
touchStart.current = { x: touch.clientX, y: touch.clientY };
|
|
220
|
-
}, []);
|
|
221
|
-
const handleTouchEnd = react.useCallback((e) => {
|
|
222
|
-
if (!touchStart.current) return;
|
|
223
|
-
const touch = e.changedTouches[0];
|
|
224
|
-
if (!touch) return;
|
|
225
|
-
const deltaX = touch.clientX - touchStart.current.x;
|
|
226
|
-
const deltaY = touch.clientY - touchStart.current.y;
|
|
227
|
-
const absX = Math.abs(deltaX);
|
|
228
|
-
const absY = Math.abs(deltaY);
|
|
229
|
-
if (absX < threshold && absY < threshold) {
|
|
230
|
-
touchStart.current = null;
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
let direction;
|
|
234
|
-
if (absX > absY) {
|
|
235
|
-
direction = deltaX > 0 ? "right" : "left";
|
|
236
|
-
} else {
|
|
237
|
-
direction = deltaY > 0 ? "down" : "up";
|
|
238
|
-
}
|
|
239
|
-
if (navigator.vibrate) {
|
|
240
|
-
navigator.vibrate(15);
|
|
241
|
-
}
|
|
242
|
-
onSwipe(direction);
|
|
243
|
-
touchStart.current = null;
|
|
244
|
-
}, [onSwipe, threshold]);
|
|
245
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
246
|
-
"div",
|
|
247
|
-
{
|
|
248
|
-
className: `${styles.swipeArea} ${className || ""}`,
|
|
249
|
-
onTouchStart: handleTouchStart,
|
|
250
|
-
onTouchEnd: handleTouchEnd,
|
|
251
|
-
onTouchCancel: () => {
|
|
252
|
-
touchStart.current = null;
|
|
253
|
-
},
|
|
254
|
-
children
|
|
255
|
-
}
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
class DirectTransport {
|
|
260
|
-
constructor(socket) {
|
|
261
|
-
this.socket = socket;
|
|
262
|
-
}
|
|
263
|
-
emit(event, ...args) {
|
|
264
|
-
this.socket.emit(event, ...args);
|
|
265
|
-
}
|
|
266
|
-
on(event, handler) {
|
|
267
|
-
this.socket.on(event, handler);
|
|
268
|
-
}
|
|
269
|
-
off(event, handler) {
|
|
270
|
-
if (handler) {
|
|
271
|
-
this.socket.off(event, handler);
|
|
272
|
-
} else {
|
|
273
|
-
this.socket.off(event);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const TransportContext = react.createContext(null);
|
|
279
|
-
function useTransport() {
|
|
280
|
-
const transport = react.useContext(TransportContext);
|
|
281
|
-
if (!transport) {
|
|
282
|
-
throw new Error("useTransport must be used within a RoomProvider that supplies a Transport");
|
|
283
|
-
}
|
|
284
|
-
return transport;
|
|
285
|
-
}
|
|
286
|
-
const RoomContext = react.createContext(null);
|
|
287
|
-
const HostRoomProvider = ({
|
|
288
|
-
roomCode,
|
|
289
|
-
players,
|
|
290
|
-
leaderId,
|
|
291
|
-
socket,
|
|
292
|
-
children
|
|
293
|
-
}) => {
|
|
294
|
-
const connectedPlayers = react.useMemo(
|
|
295
|
-
() => players.filter((p) => p.connected !== false),
|
|
296
|
-
[players]
|
|
297
|
-
);
|
|
298
|
-
const hostState = react.useMemo(
|
|
299
|
-
() => ({ roomCode, players, connectedPlayers, leaderId, socket }),
|
|
300
|
-
[roomCode, players, connectedPlayers, leaderId, socket]
|
|
301
|
-
);
|
|
302
|
-
const value = react.useMemo(
|
|
303
|
-
() => ({
|
|
304
|
-
roomCode,
|
|
305
|
-
players,
|
|
306
|
-
connectedPlayers,
|
|
307
|
-
leaderId,
|
|
308
|
-
side: "host",
|
|
309
|
-
host: hostState,
|
|
310
|
-
player: null
|
|
311
|
-
}),
|
|
312
|
-
[roomCode, players, connectedPlayers, leaderId, hostState]
|
|
313
|
-
);
|
|
314
|
-
const transport = react.useMemo(() => new DirectTransport(socket), [socket]);
|
|
315
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TransportContext.Provider, { value: transport, children: /* @__PURE__ */ jsxRuntime.jsx(RoomContext.Provider, { value, children }) });
|
|
316
|
-
};
|
|
317
|
-
const PlayerRoomProvider = ({
|
|
318
|
-
roomCode,
|
|
319
|
-
players,
|
|
320
|
-
leaderId,
|
|
321
|
-
mySessionId,
|
|
322
|
-
isLeader,
|
|
323
|
-
socket,
|
|
324
|
-
isConnected,
|
|
325
|
-
children
|
|
326
|
-
}) => {
|
|
327
|
-
const connectedPlayers = react.useMemo(
|
|
328
|
-
() => players.filter((p) => p.connected !== false),
|
|
329
|
-
[players]
|
|
330
|
-
);
|
|
331
|
-
const playerState = react.useMemo(
|
|
332
|
-
() => ({
|
|
333
|
-
roomCode,
|
|
334
|
-
players,
|
|
335
|
-
connectedPlayers,
|
|
336
|
-
leaderId,
|
|
337
|
-
mySessionId,
|
|
338
|
-
isLeader,
|
|
339
|
-
socket,
|
|
340
|
-
isConnected
|
|
341
|
-
}),
|
|
342
|
-
[roomCode, players, connectedPlayers, leaderId, mySessionId, isLeader, socket, isConnected]
|
|
343
|
-
);
|
|
344
|
-
const value = react.useMemo(
|
|
345
|
-
() => ({
|
|
346
|
-
roomCode,
|
|
347
|
-
players,
|
|
348
|
-
connectedPlayers,
|
|
349
|
-
leaderId,
|
|
350
|
-
side: "player",
|
|
351
|
-
host: null,
|
|
352
|
-
player: playerState
|
|
353
|
-
}),
|
|
354
|
-
[roomCode, players, connectedPlayers, leaderId, playerState]
|
|
355
|
-
);
|
|
356
|
-
const transport = react.useMemo(() => new DirectTransport(socket), [socket]);
|
|
357
|
-
return /* @__PURE__ */ jsxRuntime.jsx(TransportContext.Provider, { value: transport, children: /* @__PURE__ */ jsxRuntime.jsx(RoomContext.Provider, { value, children }) });
|
|
358
|
-
};
|
|
359
|
-
function useRoom() {
|
|
360
|
-
const context = react.useContext(RoomContext);
|
|
361
|
-
if (!context) {
|
|
362
|
-
throw new Error("useRoom must be used within HostRoomProvider or PlayerRoomProvider");
|
|
363
|
-
}
|
|
364
|
-
return context;
|
|
365
|
-
}
|
|
366
|
-
function useHostRoom() {
|
|
367
|
-
const context = useRoom();
|
|
368
|
-
if (context.side !== "host" || !context.host) {
|
|
369
|
-
throw new Error("useHostRoom must be used within HostRoomProvider");
|
|
370
|
-
}
|
|
371
|
-
return context.host;
|
|
372
|
-
}
|
|
373
|
-
function usePlayerRoom() {
|
|
374
|
-
const context = useRoom();
|
|
375
|
-
if (context.side !== "player" || !context.player) {
|
|
376
|
-
throw new Error("usePlayerRoom must be used within PlayerRoomProvider");
|
|
377
|
-
}
|
|
378
|
-
return context.player;
|
|
379
|
-
}
|
|
2
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
3
|
+
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
|
4
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.SmoreSDK = {}));
|
|
5
|
+
})(this, (function (exports) { 'use strict';
|
|
380
6
|
|
|
381
7
|
const SMORE_MSG_PREFIX = "smore:";
|
|
382
8
|
function isSmoreMessage(data) {
|
|
@@ -449,244 +75,912 @@
|
|
|
449
75
|
}
|
|
450
76
|
}
|
|
451
77
|
|
|
452
|
-
const SYSTEM_PREFIX = "smore:";
|
|
453
|
-
const SYSTEM_EVENTS = {
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
GAME_OVER: `${SYSTEM_PREFIX}game-over
|
|
458
|
-
RETURN_TO_LOBBY: `${SYSTEM_PREFIX}return-to-lobby`,
|
|
459
|
-
SEND_TO_PLAYER: `${SYSTEM_PREFIX}send-to-player`,
|
|
460
|
-
BROADCAST: `${SYSTEM_PREFIX}broadcast`
|
|
78
|
+
const SYSTEM_PREFIX$1 = "smore:";
|
|
79
|
+
const SYSTEM_EVENTS$1 = {
|
|
80
|
+
PLAYER_JOIN: `${SYSTEM_PREFIX$1}player-join`,
|
|
81
|
+
PLAYER_LEAVE: `${SYSTEM_PREFIX$1}player-leave`,
|
|
82
|
+
PLAYER_RECONNECT: `${SYSTEM_PREFIX$1}player-reconnect`,
|
|
83
|
+
GAME_OVER: `${SYSTEM_PREFIX$1}game-over`
|
|
461
84
|
};
|
|
462
|
-
const
|
|
85
|
+
const DEFAULT_TIMEOUT$1 = 1e4;
|
|
86
|
+
let SmoreSDKError$1 = class SmoreSDKError extends Error {
|
|
87
|
+
code;
|
|
88
|
+
cause;
|
|
89
|
+
details;
|
|
90
|
+
constructor(code, message, options) {
|
|
91
|
+
super(message);
|
|
92
|
+
this.name = "SmoreSDKError";
|
|
93
|
+
this.code = code;
|
|
94
|
+
this.cause = options?.cause;
|
|
95
|
+
this.details = options?.details;
|
|
96
|
+
const ErrorWithCapture = Error;
|
|
97
|
+
if (typeof ErrorWithCapture.captureStackTrace === "function") {
|
|
98
|
+
ErrorWithCapture.captureStackTrace(this, SmoreSDKError);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
toSmoreError() {
|
|
102
|
+
return {
|
|
103
|
+
code: this.code,
|
|
104
|
+
message: this.message,
|
|
105
|
+
cause: this.cause,
|
|
106
|
+
details: this.details
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const EVENT_NAME_REGEX$1 = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
|
|
463
111
|
function validateEventName$1(event) {
|
|
112
|
+
if (!event || typeof event !== "string") {
|
|
113
|
+
throw new SmoreSDKError$1("INVALID_EVENT", "Event name must be a non-empty string");
|
|
114
|
+
}
|
|
464
115
|
if (!EVENT_NAME_REGEX$1.test(event)) {
|
|
465
|
-
throw new
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
116
|
+
throw new SmoreSDKError$1(
|
|
117
|
+
"INVALID_EVENT",
|
|
118
|
+
`Invalid event name "${event}". Event names must start with a letter, contain only letters, numbers, hyphens, or underscores, and end with a letter or number.`,
|
|
119
|
+
{ details: { event } }
|
|
469
120
|
);
|
|
470
121
|
}
|
|
471
122
|
}
|
|
472
|
-
function
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
123
|
+
function validatePlayerIndex(playerIndex, controllersCount) {
|
|
124
|
+
if (typeof playerIndex !== "number" || !Number.isInteger(playerIndex)) {
|
|
125
|
+
throw new SmoreSDKError$1("INVALID_PLAYER", "Player index must be an integer");
|
|
126
|
+
}
|
|
127
|
+
if (playerIndex < 0 || playerIndex >= controllersCount) {
|
|
128
|
+
throw new SmoreSDKError$1(
|
|
129
|
+
"INVALID_PLAYER",
|
|
130
|
+
`Invalid player index ${playerIndex}. Valid range: 0-${controllersCount - 1}`,
|
|
131
|
+
{ details: { playerIndex, controllersCount } }
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
class DebugLogger {
|
|
136
|
+
enabled;
|
|
137
|
+
level;
|
|
138
|
+
prefix;
|
|
139
|
+
logSend;
|
|
140
|
+
logReceive;
|
|
141
|
+
logLifecycle;
|
|
142
|
+
customLogger;
|
|
143
|
+
static levelOrder = {
|
|
144
|
+
debug: 0,
|
|
145
|
+
info: 1,
|
|
146
|
+
warn: 2,
|
|
147
|
+
error: 3
|
|
148
|
+
};
|
|
149
|
+
constructor(options) {
|
|
150
|
+
if (typeof options === "boolean") {
|
|
151
|
+
this.enabled = options;
|
|
152
|
+
this.level = "debug";
|
|
153
|
+
this.prefix = "[SmoreScreen]";
|
|
154
|
+
this.logSend = true;
|
|
155
|
+
this.logReceive = true;
|
|
156
|
+
this.logLifecycle = true;
|
|
157
|
+
} else if (options) {
|
|
158
|
+
this.enabled = options.enabled ?? false;
|
|
159
|
+
this.level = options.level ?? "debug";
|
|
160
|
+
this.prefix = options.prefix ?? "[SmoreScreen]";
|
|
161
|
+
this.logSend = options.logSend ?? true;
|
|
162
|
+
this.logReceive = options.logReceive ?? true;
|
|
163
|
+
this.logLifecycle = options.logLifecycle ?? true;
|
|
164
|
+
this.customLogger = options.logger;
|
|
165
|
+
} else {
|
|
166
|
+
this.enabled = false;
|
|
167
|
+
this.level = "debug";
|
|
168
|
+
this.prefix = "[SmoreScreen]";
|
|
169
|
+
this.logSend = true;
|
|
170
|
+
this.logReceive = true;
|
|
171
|
+
this.logLifecycle = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
shouldLog(level) {
|
|
175
|
+
return this.enabled && DebugLogger.levelOrder[level] >= DebugLogger.levelOrder[this.level];
|
|
176
|
+
}
|
|
177
|
+
log(level, message, data) {
|
|
178
|
+
if (!this.shouldLog(level)) return;
|
|
179
|
+
if (this.customLogger) {
|
|
180
|
+
this.customLogger(level, `${this.prefix} ${message}`, data);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
const consoleMethod = level === "error" ? "error" : level === "warn" ? "warn" : "log";
|
|
184
|
+
if (data !== void 0) {
|
|
185
|
+
console[consoleMethod](`${this.prefix} ${message}`, data);
|
|
186
|
+
} else {
|
|
187
|
+
console[consoleMethod](`${this.prefix} ${message}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
debug(message, data) {
|
|
191
|
+
this.log("debug", message, data);
|
|
192
|
+
}
|
|
193
|
+
info(message, data) {
|
|
194
|
+
this.log("info", message, data);
|
|
195
|
+
}
|
|
196
|
+
warn(message, data) {
|
|
197
|
+
this.log("warn", message, data);
|
|
198
|
+
}
|
|
199
|
+
error(message, data) {
|
|
200
|
+
this.log("error", message, data);
|
|
201
|
+
}
|
|
202
|
+
send(event, data) {
|
|
203
|
+
if (this.logSend) {
|
|
204
|
+
this.debug(`-> SEND: ${event}`, data);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
receive(event, data) {
|
|
208
|
+
if (this.logReceive) {
|
|
209
|
+
this.debug(`<- RECV: ${event}`, data);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
lifecycle(message, data) {
|
|
213
|
+
if (this.logLifecycle) {
|
|
214
|
+
this.info(`[Lifecycle] ${message}`, data);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
class ScreenImpl {
|
|
219
|
+
transport = null;
|
|
220
|
+
config;
|
|
221
|
+
logger;
|
|
222
|
+
_controllers = [];
|
|
223
|
+
_roomCode = "";
|
|
224
|
+
_leaderIndex = -1;
|
|
225
|
+
_isReady = false;
|
|
226
|
+
_isDestroyed = false;
|
|
227
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
228
|
+
registeredTransportHandlers = [];
|
|
229
|
+
boundMessageHandler = null;
|
|
230
|
+
constructor(config = {}) {
|
|
231
|
+
this.config = config;
|
|
232
|
+
this.logger = new DebugLogger(config.debug);
|
|
233
|
+
if (config.listeners) {
|
|
234
|
+
for (const event of Object.keys(config.listeners)) {
|
|
235
|
+
validateEventName$1(event);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Initialization (called by factory)
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
async initialize() {
|
|
243
|
+
this.logger.lifecycle("Initializing screen...");
|
|
244
|
+
const parentOrigin = this.config.parentOrigin ?? "*";
|
|
245
|
+
const timeout = this.config.timeout ?? DEFAULT_TIMEOUT$1;
|
|
246
|
+
return new Promise((resolve, reject) => {
|
|
247
|
+
const timeoutId = setTimeout(() => {
|
|
248
|
+
this.cleanup();
|
|
249
|
+
const error = new SmoreSDKError$1(
|
|
250
|
+
"TIMEOUT",
|
|
251
|
+
`Screen initialization timed out after ${timeout}ms. Make sure the parent frame sends smore:init.`,
|
|
252
|
+
{ details: { timeout } }
|
|
253
|
+
);
|
|
254
|
+
this.handleError(error);
|
|
255
|
+
reject(error);
|
|
256
|
+
}, timeout);
|
|
257
|
+
this.boundMessageHandler = (e) => {
|
|
258
|
+
if (parentOrigin !== "*" && e.origin !== parentOrigin) return;
|
|
259
|
+
const msg = e.data;
|
|
260
|
+
if (!isSmoreMessage(msg)) return;
|
|
261
|
+
if (msg.type === "smore:init") {
|
|
262
|
+
clearTimeout(timeoutId);
|
|
263
|
+
const initData = msg.payload;
|
|
264
|
+
if (initData.side !== "host") {
|
|
265
|
+
const error = new SmoreSDKError$1(
|
|
266
|
+
"INIT_FAILED",
|
|
267
|
+
`Received init for wrong side: ${initData.side}. Expected "host".`,
|
|
268
|
+
{ details: { side: initData.side } }
|
|
269
|
+
);
|
|
270
|
+
this.handleError(error);
|
|
271
|
+
reject(error);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
this.transport = new PostMessageTransport(parentOrigin);
|
|
275
|
+
this._roomCode = initData.roomCode;
|
|
276
|
+
this._controllers = this.mapControllersFromInit(initData.players);
|
|
277
|
+
this._leaderIndex = this.findLeaderIndex(initData.players, initData.leaderId);
|
|
278
|
+
this.setupEventHandlers();
|
|
279
|
+
this._isReady = true;
|
|
280
|
+
this.logger.lifecycle("Screen ready", {
|
|
281
|
+
roomCode: this._roomCode,
|
|
282
|
+
controllers: this._controllers.length,
|
|
283
|
+
leaderIndex: this._leaderIndex
|
|
284
|
+
});
|
|
285
|
+
this.config.onReady?.();
|
|
286
|
+
resolve();
|
|
287
|
+
} else if (msg.type === "smore:update") {
|
|
288
|
+
const updateData = msg.payload;
|
|
289
|
+
if (updateData.players) {
|
|
290
|
+
this._controllers = this.mapControllersFromInit(updateData.players);
|
|
291
|
+
}
|
|
292
|
+
if (updateData.leaderId !== void 0) {
|
|
293
|
+
this._leaderIndex = this.findLeaderIndex(
|
|
294
|
+
updateData.players ?? [],
|
|
295
|
+
updateData.leaderId
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
this.logger.lifecycle("Room updated", {
|
|
299
|
+
controllers: this._controllers.length,
|
|
300
|
+
leaderIndex: this._leaderIndex
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
};
|
|
304
|
+
window.addEventListener("message", this.boundMessageHandler);
|
|
305
|
+
window.parent.postMessage({ type: "smore:ready" }, parentOrigin);
|
|
306
|
+
this.logger.lifecycle("Sent smore:ready to parent");
|
|
508
307
|
});
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
308
|
+
}
|
|
309
|
+
mapControllersFromInit(players) {
|
|
310
|
+
return players.map((p, index) => ({
|
|
311
|
+
playerIndex: p.playerIndex ?? index,
|
|
312
|
+
nickname: p.nickname || `Player ${index + 1}`,
|
|
313
|
+
connected: p.connected !== false,
|
|
314
|
+
appearance: p.appearance
|
|
315
|
+
}));
|
|
316
|
+
}
|
|
317
|
+
findLeaderIndex(players, leaderId) {
|
|
318
|
+
if (!leaderId) return -1;
|
|
319
|
+
const idx = players.findIndex(
|
|
320
|
+
(p) => p.sessionId === leaderId
|
|
321
|
+
);
|
|
322
|
+
return idx >= 0 ? idx : -1;
|
|
323
|
+
}
|
|
324
|
+
setupEventHandlers() {
|
|
325
|
+
if (!this.transport) return;
|
|
326
|
+
this.registerTransportHandler(SYSTEM_EVENTS$1.PLAYER_JOIN, (data) => {
|
|
327
|
+
const payload = data;
|
|
328
|
+
const player = payload?.player;
|
|
329
|
+
if (player && typeof player.playerIndex === "number") {
|
|
330
|
+
this.logger.lifecycle("Controller joined", { playerIndex: player.playerIndex });
|
|
331
|
+
this.config.onControllerJoin?.(player.playerIndex, player);
|
|
512
332
|
}
|
|
513
333
|
});
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
334
|
+
this.registerTransportHandler(SYSTEM_EVENTS$1.PLAYER_LEAVE, (data) => {
|
|
335
|
+
const payload = data;
|
|
336
|
+
if (typeof payload?.playerIndex === "number") {
|
|
337
|
+
this.logger.lifecycle("Controller left", { playerIndex: payload.playerIndex });
|
|
338
|
+
this.config.onControllerLeave?.(payload.playerIndex);
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
this.registerTransportHandler(SYSTEM_EVENTS$1.PLAYER_RECONNECT, (data) => {
|
|
342
|
+
const payload = data;
|
|
343
|
+
const player = payload?.player;
|
|
344
|
+
if (player && typeof player.playerIndex === "number") {
|
|
345
|
+
this.logger.lifecycle("Controller reconnected", { playerIndex: player.playerIndex });
|
|
346
|
+
this.config.onControllerReconnect?.(player.playerIndex, player);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
this.registerTransportHandler("room:player-joined", (data) => {
|
|
350
|
+
const payload = data;
|
|
351
|
+
const playerIndex = payload?.player?.playerIndex ?? payload?.playerIndex;
|
|
352
|
+
if (typeof playerIndex === "number") {
|
|
353
|
+
this.config.onControllerJoin?.(playerIndex, {
|
|
354
|
+
playerIndex,
|
|
355
|
+
nickname: `Player ${playerIndex + 1}`,
|
|
356
|
+
connected: true
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
this.registerTransportHandler("room:player-left", (data) => {
|
|
361
|
+
const payload = data;
|
|
362
|
+
const playerIndex = payload?.playerIndex ?? payload?.player?.playerIndex;
|
|
363
|
+
if (typeof playerIndex === "number") {
|
|
364
|
+
this.config.onControllerLeave?.(playerIndex);
|
|
365
|
+
}
|
|
366
|
+
});
|
|
367
|
+
if (this.config.listeners) {
|
|
368
|
+
for (const [event, handler] of Object.entries(this.config.listeners)) {
|
|
369
|
+
if (!handler) continue;
|
|
370
|
+
this.setupUserEventHandler(event, handler);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
setupUserEventHandler(event, handler) {
|
|
375
|
+
const wrappedHandler = (data) => {
|
|
376
|
+
this.logger.receive(event, data);
|
|
377
|
+
const payload = data;
|
|
378
|
+
const { playerIndex, ...rest } = payload;
|
|
379
|
+
if (typeof playerIndex === "number") {
|
|
380
|
+
try {
|
|
381
|
+
handler(playerIndex, rest);
|
|
382
|
+
} catch (err) {
|
|
383
|
+
this.handleError(
|
|
384
|
+
new SmoreSDKError$1("UNKNOWN", `Error in handler for event "${event}"`, {
|
|
385
|
+
cause: err instanceof Error ? err : void 0,
|
|
386
|
+
details: { event, playerIndex }
|
|
387
|
+
})
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
520
391
|
};
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
if (!
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
392
|
+
this.registerTransportHandler(event, wrappedHandler);
|
|
393
|
+
let handlers = this.eventHandlers.get(event);
|
|
394
|
+
if (!handlers) {
|
|
395
|
+
handlers = /* @__PURE__ */ new Set();
|
|
396
|
+
this.eventHandlers.set(event, handlers);
|
|
397
|
+
}
|
|
398
|
+
handlers.add(handler);
|
|
399
|
+
}
|
|
400
|
+
registerTransportHandler(event, handler) {
|
|
401
|
+
if (!this.transport) return;
|
|
402
|
+
this.transport.on(event, handler);
|
|
403
|
+
this.registeredTransportHandlers.push({ event, handler });
|
|
404
|
+
}
|
|
405
|
+
// ---------------------------------------------------------------------------
|
|
406
|
+
// Properties (readonly)
|
|
407
|
+
// ---------------------------------------------------------------------------
|
|
408
|
+
get controllers() {
|
|
409
|
+
return [...this._controllers];
|
|
410
|
+
}
|
|
411
|
+
get roomCode() {
|
|
412
|
+
return this._roomCode;
|
|
413
|
+
}
|
|
414
|
+
get leaderIndex() {
|
|
415
|
+
return this._leaderIndex;
|
|
416
|
+
}
|
|
417
|
+
get isReady() {
|
|
418
|
+
return this._isReady;
|
|
419
|
+
}
|
|
420
|
+
get isDestroyed() {
|
|
421
|
+
return this._isDestroyed;
|
|
422
|
+
}
|
|
423
|
+
// ---------------------------------------------------------------------------
|
|
424
|
+
// Communication Methods
|
|
425
|
+
// ---------------------------------------------------------------------------
|
|
426
|
+
broadcast(event, data) {
|
|
427
|
+
this.ensureReady("broadcast");
|
|
428
|
+
validateEventName$1(event);
|
|
429
|
+
this.logger.send(event, data);
|
|
430
|
+
this.transport.emit(event, data);
|
|
431
|
+
}
|
|
432
|
+
broadcastRaw(event, data) {
|
|
433
|
+
this.ensureReady("broadcastRaw");
|
|
434
|
+
validateEventName$1(event);
|
|
435
|
+
this.logger.send(event, data);
|
|
436
|
+
this.transport.emit(event, data);
|
|
437
|
+
}
|
|
438
|
+
sendToController(playerIndex, event, data) {
|
|
439
|
+
this.ensureReady("sendToController");
|
|
440
|
+
validateEventName$1(event);
|
|
441
|
+
validatePlayerIndex(playerIndex, this._controllers.length);
|
|
442
|
+
this.logger.send(`${event} -> Player ${playerIndex}`, data);
|
|
443
|
+
this.transport.emit(event, {
|
|
444
|
+
targetPlayerIndex: playerIndex,
|
|
445
|
+
...data && typeof data === "object" ? data : { data }
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
sendToControllerRaw(playerIndex, event, data) {
|
|
449
|
+
this.ensureReady("sendToControllerRaw");
|
|
450
|
+
validateEventName$1(event);
|
|
451
|
+
validatePlayerIndex(playerIndex, this._controllers.length);
|
|
452
|
+
this.logger.send(`${event} -> Player ${playerIndex}`, data);
|
|
453
|
+
this.transport.emit(event, {
|
|
454
|
+
targetPlayerIndex: playerIndex,
|
|
455
|
+
...data && typeof data === "object" ? data : { data }
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
// ---------------------------------------------------------------------------
|
|
459
|
+
// Game Lifecycle
|
|
460
|
+
// ---------------------------------------------------------------------------
|
|
461
|
+
gameOver(results) {
|
|
462
|
+
this.ensureReady("gameOver");
|
|
463
|
+
this.logger.lifecycle("Game over", results);
|
|
464
|
+
this.transport.emit(SYSTEM_EVENTS$1.GAME_OVER, { results });
|
|
465
|
+
}
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
// Event Subscription
|
|
468
|
+
// ---------------------------------------------------------------------------
|
|
469
|
+
on(event, handler) {
|
|
470
|
+
validateEventName$1(event);
|
|
471
|
+
let handlers = this.eventHandlers.get(event);
|
|
472
|
+
if (!handlers) {
|
|
473
|
+
handlers = /* @__PURE__ */ new Set();
|
|
474
|
+
this.eventHandlers.set(event, handlers);
|
|
475
|
+
}
|
|
476
|
+
handlers.add(handler);
|
|
477
|
+
if (this.transport) {
|
|
529
478
|
const wrappedHandler = (data) => {
|
|
530
|
-
|
|
531
|
-
|
|
479
|
+
this.logger.receive(event, data);
|
|
480
|
+
const payload = data;
|
|
481
|
+
const { playerIndex, ...rest } = payload;
|
|
482
|
+
if (typeof playerIndex === "number") {
|
|
483
|
+
try {
|
|
484
|
+
handler(playerIndex, rest);
|
|
485
|
+
} catch (err) {
|
|
486
|
+
this.handleError(
|
|
487
|
+
new SmoreSDKError$1("UNKNOWN", `Error in handler for event "${event}"`, {
|
|
488
|
+
cause: err instanceof Error ? err : void 0
|
|
489
|
+
})
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
532
493
|
};
|
|
533
|
-
|
|
534
|
-
cleanups.push(() => transport.off(event, wrappedHandler));
|
|
494
|
+
this.registerTransportHandler(event, wrappedHandler);
|
|
535
495
|
}
|
|
536
496
|
return () => {
|
|
537
|
-
|
|
497
|
+
handlers?.delete(handler);
|
|
498
|
+
if (handlers?.size === 0) {
|
|
499
|
+
this.eventHandlers.delete(event);
|
|
500
|
+
}
|
|
538
501
|
};
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
(
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
)
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
502
|
+
}
|
|
503
|
+
once(event, handler) {
|
|
504
|
+
const wrappedHandler = (playerIndex, data) => {
|
|
505
|
+
unsubscribe();
|
|
506
|
+
handler(playerIndex, data);
|
|
507
|
+
};
|
|
508
|
+
const unsubscribe = this.on(event, wrappedHandler);
|
|
509
|
+
return unsubscribe;
|
|
510
|
+
}
|
|
511
|
+
off(event, handler) {
|
|
512
|
+
if (!handler) {
|
|
513
|
+
this.eventHandlers.delete(event);
|
|
514
|
+
this.transport?.off(event);
|
|
515
|
+
} else {
|
|
516
|
+
const handlers = this.eventHandlers.get(event);
|
|
517
|
+
handlers?.delete(handler);
|
|
518
|
+
if (handlers?.size === 0) {
|
|
519
|
+
this.eventHandlers.delete(event);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// ---------------------------------------------------------------------------
|
|
524
|
+
// Utilities
|
|
525
|
+
// ---------------------------------------------------------------------------
|
|
526
|
+
getController(playerIndex) {
|
|
527
|
+
return this._controllers.find((c) => c.playerIndex === playerIndex);
|
|
528
|
+
}
|
|
529
|
+
isLeader(playerIndex) {
|
|
530
|
+
return playerIndex === this._leaderIndex;
|
|
531
|
+
}
|
|
532
|
+
getControllerCount() {
|
|
533
|
+
return this._controllers.filter((c) => c.connected).length;
|
|
534
|
+
}
|
|
535
|
+
// ---------------------------------------------------------------------------
|
|
536
|
+
// Cleanup
|
|
537
|
+
// ---------------------------------------------------------------------------
|
|
538
|
+
destroy() {
|
|
539
|
+
if (this._isDestroyed) return;
|
|
540
|
+
this.logger.lifecycle("Destroying screen...");
|
|
541
|
+
this._isDestroyed = true;
|
|
542
|
+
this._isReady = false;
|
|
543
|
+
this.cleanup();
|
|
544
|
+
this.logger.lifecycle("Screen destroyed");
|
|
545
|
+
}
|
|
546
|
+
cleanup() {
|
|
547
|
+
for (const { event, handler } of this.registeredTransportHandlers) {
|
|
548
|
+
this.transport?.off(event, handler);
|
|
549
|
+
}
|
|
550
|
+
this.registeredTransportHandlers = [];
|
|
551
|
+
this.eventHandlers.clear();
|
|
552
|
+
if (this.transport instanceof PostMessageTransport) {
|
|
553
|
+
this.transport.destroy();
|
|
554
|
+
}
|
|
555
|
+
this.transport = null;
|
|
556
|
+
if (this.boundMessageHandler) {
|
|
557
|
+
window.removeEventListener("message", this.boundMessageHandler);
|
|
558
|
+
this.boundMessageHandler = null;
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
// ---------------------------------------------------------------------------
|
|
562
|
+
// Error Handling
|
|
563
|
+
// ---------------------------------------------------------------------------
|
|
564
|
+
handleError(error) {
|
|
565
|
+
const smoreError = error.toSmoreError();
|
|
566
|
+
if (this.config.onError) {
|
|
567
|
+
this.config.onError(smoreError);
|
|
568
|
+
} else {
|
|
569
|
+
this.logger.error(error.message, error.details);
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
ensureReady(method) {
|
|
573
|
+
if (this._isDestroyed) {
|
|
574
|
+
throw new SmoreSDKError$1(
|
|
575
|
+
"DESTROYED",
|
|
576
|
+
`Cannot call ${method}() after destroy()`,
|
|
577
|
+
{ details: { method } }
|
|
578
|
+
);
|
|
579
|
+
}
|
|
580
|
+
if (!this._isReady || !this.transport) {
|
|
581
|
+
throw new SmoreSDKError$1(
|
|
582
|
+
"NOT_READY",
|
|
583
|
+
`Cannot call ${method}() before screen is ready. Use await createScreen() or onReady callback.`,
|
|
584
|
+
{ details: { method } }
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
function createScreen(config) {
|
|
590
|
+
const screen = new ScreenImpl(config);
|
|
591
|
+
const promise = screen.initialize().then(() => screen);
|
|
592
|
+
promise.instance = screen;
|
|
593
|
+
return promise;
|
|
576
594
|
}
|
|
577
595
|
|
|
578
|
-
const
|
|
596
|
+
const SYSTEM_PREFIX = "smore:";
|
|
597
|
+
const SYSTEM_EVENTS = {
|
|
598
|
+
PLAYER_JOIN: `${SYSTEM_PREFIX}player-join`,
|
|
599
|
+
PLAYER_LEAVE: `${SYSTEM_PREFIX}player-leave`
|
|
600
|
+
};
|
|
601
|
+
const DEFAULT_TIMEOUT = 1e4;
|
|
602
|
+
class SmoreSDKError extends Error {
|
|
603
|
+
code;
|
|
604
|
+
cause;
|
|
605
|
+
details;
|
|
606
|
+
constructor(code, message, options) {
|
|
607
|
+
super(message);
|
|
608
|
+
this.name = "SmoreSDKError";
|
|
609
|
+
this.code = code;
|
|
610
|
+
this.cause = options?.cause;
|
|
611
|
+
this.details = options?.details;
|
|
612
|
+
const ErrorWithCapture = Error;
|
|
613
|
+
if (typeof ErrorWithCapture.captureStackTrace === "function") {
|
|
614
|
+
ErrorWithCapture.captureStackTrace(this, SmoreSDKError);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
toSmoreError() {
|
|
618
|
+
return {
|
|
619
|
+
code: this.code,
|
|
620
|
+
message: this.message,
|
|
621
|
+
cause: this.cause,
|
|
622
|
+
details: this.details
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
const EVENT_NAME_REGEX = /^[a-zA-Z]([a-zA-Z0-9_-]*[a-zA-Z0-9])?$/;
|
|
579
627
|
function validateEventName(event) {
|
|
628
|
+
if (!event || typeof event !== "string") {
|
|
629
|
+
throw new SmoreSDKError("INVALID_EVENT", "Event name must be a non-empty string");
|
|
630
|
+
}
|
|
580
631
|
if (!EVENT_NAME_REGEX.test(event)) {
|
|
581
|
-
throw new
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
- Start
|
|
632
|
+
throw new SmoreSDKError(
|
|
633
|
+
"INVALID_EVENT",
|
|
634
|
+
`Invalid event name "${event}". Event names must:
|
|
635
|
+
- Start with a letter (a-z, A-Z)
|
|
636
|
+
- Only contain letters, numbers, hyphens (-), and underscores (_)
|
|
637
|
+
- End with a letter or number`,
|
|
638
|
+
{ details: { event } }
|
|
585
639
|
);
|
|
586
640
|
}
|
|
587
641
|
}
|
|
588
|
-
function
|
|
589
|
-
const
|
|
590
|
-
const
|
|
591
|
-
const
|
|
592
|
-
const
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
transport.off("smore:player-leave", handlePlayerLeave);
|
|
618
|
-
};
|
|
619
|
-
}, [transport]);
|
|
620
|
-
react.useEffect(() => {
|
|
621
|
-
if (!transport || !listenersRef.current) return;
|
|
622
|
-
const cleanups = [];
|
|
623
|
-
Object.entries(listenersRef.current).forEach(([event, handler]) => {
|
|
624
|
-
if (handler) {
|
|
625
|
-
transport.on(event, handler);
|
|
626
|
-
cleanups.push(() => transport.off(event, handler));
|
|
627
|
-
}
|
|
628
|
-
});
|
|
629
|
-
return () => cleanups.forEach((fn) => fn());
|
|
630
|
-
}, [transport, listeners]);
|
|
631
|
-
const emit = react.useCallback(
|
|
632
|
-
(event, data) => {
|
|
633
|
-
if (!transport) return;
|
|
634
|
-
validateEventName(event);
|
|
635
|
-
transport.emit(event, data);
|
|
636
|
-
},
|
|
637
|
-
[transport]
|
|
638
|
-
);
|
|
642
|
+
function createLogger(options) {
|
|
643
|
+
const enabled = typeof options === "boolean" ? options : options?.enabled ?? false;
|
|
644
|
+
const level = (typeof options === "object" ? options.level : void 0) ?? "debug";
|
|
645
|
+
const prefix = (typeof options === "object" ? options.prefix : void 0) ?? "[SmoreController]";
|
|
646
|
+
const customLogger = typeof options === "object" ? options.logger : void 0;
|
|
647
|
+
const levelPriority = {
|
|
648
|
+
debug: 0,
|
|
649
|
+
info: 1,
|
|
650
|
+
warn: 2,
|
|
651
|
+
error: 3
|
|
652
|
+
};
|
|
653
|
+
const shouldLog = (msgLevel) => {
|
|
654
|
+
if (!enabled) return false;
|
|
655
|
+
return levelPriority[msgLevel] >= levelPriority[level];
|
|
656
|
+
};
|
|
657
|
+
const log = (msgLevel, message, data) => {
|
|
658
|
+
if (!shouldLog(msgLevel)) return;
|
|
659
|
+
if (customLogger) {
|
|
660
|
+
customLogger(msgLevel, message, data);
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
const fullMessage = `${prefix} ${message}`;
|
|
664
|
+
const consoleFn = console[msgLevel] ?? console.log;
|
|
665
|
+
if (data !== void 0) {
|
|
666
|
+
consoleFn(fullMessage, data);
|
|
667
|
+
} else {
|
|
668
|
+
consoleFn(fullMessage);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
639
671
|
return {
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
isLeader: room.isLeader,
|
|
645
|
-
emit
|
|
672
|
+
debug: (msg, data) => log("debug", msg, data),
|
|
673
|
+
info: (msg, data) => log("info", msg, data),
|
|
674
|
+
warn: (msg, data) => log("warn", msg, data),
|
|
675
|
+
error: (msg, data) => log("error", msg, data)
|
|
646
676
|
};
|
|
647
677
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
678
|
+
class ControllerImpl {
|
|
679
|
+
transport = null;
|
|
680
|
+
config;
|
|
681
|
+
logger;
|
|
682
|
+
_roomCode = "";
|
|
683
|
+
_myIndex = -1;
|
|
684
|
+
_isLeader = false;
|
|
685
|
+
_isReady = false;
|
|
686
|
+
_isDestroyed = false;
|
|
687
|
+
boundMessageHandler = null;
|
|
688
|
+
registeredHandlers = [];
|
|
689
|
+
eventListeners = /* @__PURE__ */ new Map();
|
|
690
|
+
constructor(config = {}) {
|
|
691
|
+
this.config = config;
|
|
692
|
+
this.logger = createLogger(config.debug);
|
|
693
|
+
if (config.listeners) {
|
|
694
|
+
for (const event of Object.keys(config.listeners)) {
|
|
695
|
+
validateEventName(event);
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
// ---------------------------------------------------------------------------
|
|
700
|
+
// Properties (readonly)
|
|
701
|
+
// ---------------------------------------------------------------------------
|
|
702
|
+
get myIndex() {
|
|
703
|
+
return this._myIndex;
|
|
704
|
+
}
|
|
705
|
+
get isLeader() {
|
|
706
|
+
return this._isLeader;
|
|
707
|
+
}
|
|
708
|
+
get roomCode() {
|
|
709
|
+
return this._roomCode;
|
|
710
|
+
}
|
|
711
|
+
get isReady() {
|
|
712
|
+
return this._isReady;
|
|
713
|
+
}
|
|
714
|
+
get isDestroyed() {
|
|
715
|
+
return this._isDestroyed;
|
|
716
|
+
}
|
|
717
|
+
// ---------------------------------------------------------------------------
|
|
718
|
+
// Initialization
|
|
719
|
+
// ---------------------------------------------------------------------------
|
|
720
|
+
async initialize() {
|
|
721
|
+
const parentOrigin = this.config.parentOrigin ?? "*";
|
|
722
|
+
const timeout = this.config.timeout ?? DEFAULT_TIMEOUT;
|
|
723
|
+
this.logger.debug("Initializing controller...", { parentOrigin, timeout });
|
|
724
|
+
return new Promise((resolve, reject) => {
|
|
725
|
+
const timeoutId = setTimeout(() => {
|
|
726
|
+
this.cleanup();
|
|
727
|
+
const error = new SmoreSDKError(
|
|
728
|
+
"TIMEOUT",
|
|
729
|
+
`Controller initialization timed out after ${timeout}ms. Make sure the parent window sends smore:init message.`,
|
|
730
|
+
{ details: { timeout } }
|
|
731
|
+
);
|
|
732
|
+
this.handleError(error);
|
|
733
|
+
reject(error);
|
|
734
|
+
}, timeout);
|
|
735
|
+
this.boundMessageHandler = (e) => {
|
|
736
|
+
if (parentOrigin !== "*" && e.origin !== parentOrigin) return;
|
|
737
|
+
const msg = e.data;
|
|
738
|
+
if (!isSmoreMessage(msg)) return;
|
|
739
|
+
if (msg.type === "smore:init") {
|
|
740
|
+
clearTimeout(timeoutId);
|
|
741
|
+
this.handleInit(msg, parentOrigin, resolve, reject);
|
|
742
|
+
} else if (msg.type === "smore:update") {
|
|
743
|
+
this.handleUpdate(msg);
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
window.addEventListener("message", this.boundMessageHandler);
|
|
747
|
+
this.logger.debug("Sending smore:ready to parent");
|
|
748
|
+
window.parent.postMessage({ type: "smore:ready" }, parentOrigin);
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
handleInit(msg, parentOrigin, resolve, reject) {
|
|
752
|
+
const initData = msg.payload;
|
|
753
|
+
this.logger.debug("Received smore:init", initData);
|
|
754
|
+
if (initData.side !== "player") {
|
|
755
|
+
const error = new SmoreSDKError(
|
|
756
|
+
"INIT_FAILED",
|
|
757
|
+
`Controller received init for wrong side: ${initData.side}`,
|
|
758
|
+
{ details: { side: initData.side } }
|
|
759
|
+
);
|
|
760
|
+
this.handleError(error);
|
|
761
|
+
reject(error);
|
|
762
|
+
return;
|
|
763
|
+
}
|
|
764
|
+
if (initData.myIndex === void 0) {
|
|
765
|
+
const error = new SmoreSDKError(
|
|
766
|
+
"INIT_FAILED",
|
|
767
|
+
"Missing myIndex in init payload",
|
|
768
|
+
{ details: initData }
|
|
769
|
+
);
|
|
770
|
+
this.handleError(error);
|
|
771
|
+
reject(error);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
this.transport = new PostMessageTransport(parentOrigin);
|
|
775
|
+
this._roomCode = initData.roomCode;
|
|
776
|
+
this._myIndex = initData.myIndex;
|
|
777
|
+
this._isLeader = initData.isLeader ?? false;
|
|
778
|
+
this.setupEventHandlers();
|
|
779
|
+
this._isReady = true;
|
|
780
|
+
this.logger.info("Controller ready", {
|
|
781
|
+
roomCode: this._roomCode,
|
|
782
|
+
myIndex: this._myIndex,
|
|
783
|
+
isLeader: this._isLeader
|
|
683
784
|
});
|
|
785
|
+
this.config.onReady?.();
|
|
786
|
+
resolve();
|
|
787
|
+
}
|
|
788
|
+
handleUpdate(msg) {
|
|
789
|
+
const updateData = msg.payload;
|
|
790
|
+
this.logger.debug("Received smore:update", updateData);
|
|
791
|
+
}
|
|
792
|
+
setupEventHandlers() {
|
|
793
|
+
if (!this.transport) return;
|
|
794
|
+
this.registerHandler(
|
|
795
|
+
SYSTEM_EVENTS.PLAYER_JOIN,
|
|
796
|
+
(data) => {
|
|
797
|
+
const playerIndex = data.player?.playerIndex ?? data.playerIndex;
|
|
798
|
+
if (playerIndex !== void 0) {
|
|
799
|
+
this.logger.debug("Player joined", { playerIndex });
|
|
800
|
+
this.config.onControllerJoin?.(playerIndex, data.player);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
);
|
|
804
|
+
this.registerHandler(
|
|
805
|
+
SYSTEM_EVENTS.PLAYER_LEAVE,
|
|
806
|
+
(data) => {
|
|
807
|
+
const playerIndex = data.player?.playerIndex ?? data.playerIndex;
|
|
808
|
+
if (playerIndex !== void 0) {
|
|
809
|
+
this.logger.debug("Player left", { playerIndex });
|
|
810
|
+
this.config.onControllerLeave?.(playerIndex);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
);
|
|
814
|
+
if (this.config.listeners) {
|
|
815
|
+
for (const [event, handler] of Object.entries(this.config.listeners)) {
|
|
816
|
+
if (!handler) continue;
|
|
817
|
+
this.registerHandler(event, (data) => {
|
|
818
|
+
this.logReceive(event, data);
|
|
819
|
+
handler(data);
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
registerHandler(event, handler) {
|
|
825
|
+
if (!this.transport) return;
|
|
826
|
+
this.transport.on(event, handler);
|
|
827
|
+
this.registeredHandlers.push({ event, handler });
|
|
828
|
+
}
|
|
829
|
+
// ---------------------------------------------------------------------------
|
|
830
|
+
// Communication Methods
|
|
831
|
+
// ---------------------------------------------------------------------------
|
|
832
|
+
send(event, data) {
|
|
833
|
+
this.ensureReady("send");
|
|
834
|
+
validateEventName(event);
|
|
835
|
+
this.logSend(event, data);
|
|
836
|
+
this.transport.emit(event, data);
|
|
837
|
+
}
|
|
838
|
+
sendRaw(event, data) {
|
|
839
|
+
this.ensureReady("sendRaw");
|
|
840
|
+
validateEventName(event);
|
|
841
|
+
this.logSend(event, data);
|
|
842
|
+
this.transport.emit(event, data);
|
|
843
|
+
}
|
|
844
|
+
// ---------------------------------------------------------------------------
|
|
845
|
+
// Event Subscription
|
|
846
|
+
// ---------------------------------------------------------------------------
|
|
847
|
+
on(event, handler) {
|
|
848
|
+
validateEventName(event);
|
|
849
|
+
let listeners = this.eventListeners.get(event);
|
|
850
|
+
if (!listeners) {
|
|
851
|
+
listeners = /* @__PURE__ */ new Set();
|
|
852
|
+
this.eventListeners.set(event, listeners);
|
|
853
|
+
}
|
|
854
|
+
listeners.add(handler);
|
|
855
|
+
const transportHandler = (data) => {
|
|
856
|
+
this.logReceive(event, data);
|
|
857
|
+
handler(data);
|
|
858
|
+
};
|
|
859
|
+
if (this.transport) {
|
|
860
|
+
this.transport.on(event, transportHandler);
|
|
861
|
+
this.registeredHandlers.push({ event, handler: transportHandler });
|
|
862
|
+
}
|
|
684
863
|
return () => {
|
|
685
|
-
|
|
864
|
+
listeners?.delete(handler);
|
|
865
|
+
if (listeners?.size === 0) {
|
|
866
|
+
this.eventListeners.delete(event);
|
|
867
|
+
}
|
|
868
|
+
this.transport?.off(event, transportHandler);
|
|
869
|
+
this.registeredHandlers = this.registeredHandlers.filter(
|
|
870
|
+
(h) => h.handler !== transportHandler
|
|
871
|
+
);
|
|
686
872
|
};
|
|
687
|
-
}
|
|
688
|
-
|
|
689
|
-
|
|
873
|
+
}
|
|
874
|
+
once(event, handler) {
|
|
875
|
+
const unsubscribe = this.on(event, ((data) => {
|
|
876
|
+
unsubscribe();
|
|
877
|
+
handler(data);
|
|
878
|
+
}));
|
|
879
|
+
return unsubscribe;
|
|
880
|
+
}
|
|
881
|
+
off(event, handler) {
|
|
882
|
+
if (!handler) {
|
|
883
|
+
this.eventListeners.delete(event);
|
|
884
|
+
this.transport?.off(event);
|
|
885
|
+
this.registeredHandlers = this.registeredHandlers.filter((h) => h.event !== event);
|
|
886
|
+
} else {
|
|
887
|
+
const listeners = this.eventListeners.get(event);
|
|
888
|
+
listeners?.delete(handler);
|
|
889
|
+
if (listeners?.size === 0) {
|
|
890
|
+
this.eventListeners.delete(event);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
// ---------------------------------------------------------------------------
|
|
895
|
+
// Cleanup
|
|
896
|
+
// ---------------------------------------------------------------------------
|
|
897
|
+
destroy() {
|
|
898
|
+
if (this._isDestroyed) return;
|
|
899
|
+
this.logger.info("Destroying controller");
|
|
900
|
+
this.cleanup();
|
|
901
|
+
this._isDestroyed = true;
|
|
902
|
+
}
|
|
903
|
+
cleanup() {
|
|
904
|
+
this._isReady = false;
|
|
905
|
+
for (const { event, handler } of this.registeredHandlers) {
|
|
906
|
+
this.transport?.off(event, handler);
|
|
907
|
+
}
|
|
908
|
+
this.registeredHandlers = [];
|
|
909
|
+
this.eventListeners.clear();
|
|
910
|
+
if (this.transport) {
|
|
911
|
+
this.transport.destroy();
|
|
912
|
+
this.transport = null;
|
|
913
|
+
}
|
|
914
|
+
if (this.boundMessageHandler) {
|
|
915
|
+
window.removeEventListener("message", this.boundMessageHandler);
|
|
916
|
+
this.boundMessageHandler = null;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
// ---------------------------------------------------------------------------
|
|
920
|
+
// Private Helpers
|
|
921
|
+
// ---------------------------------------------------------------------------
|
|
922
|
+
ensureReady(method) {
|
|
923
|
+
if (this._isDestroyed) {
|
|
924
|
+
throw new SmoreSDKError(
|
|
925
|
+
"DESTROYED",
|
|
926
|
+
`Cannot call ${method}() after destroy()`,
|
|
927
|
+
{ details: { method } }
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
if (!this._isReady || !this.transport) {
|
|
931
|
+
throw new SmoreSDKError(
|
|
932
|
+
"NOT_READY",
|
|
933
|
+
`Cannot call ${method}() before controller is ready. Use await createController() or wait for onReady callback.`,
|
|
934
|
+
{ details: { method, isReady: this._isReady } }
|
|
935
|
+
);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
handleError(error) {
|
|
939
|
+
if (this.config.onError) {
|
|
940
|
+
this.config.onError(error.toSmoreError());
|
|
941
|
+
} else {
|
|
942
|
+
this.logger.error(error.message, error.details);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
logSend(event, data) {
|
|
946
|
+
const options = this.config.debug;
|
|
947
|
+
const shouldLog = typeof options === "object" ? options.logSend ?? true : Boolean(options);
|
|
948
|
+
if (shouldLog) {
|
|
949
|
+
this.logger.debug(`\u2192 SEND [${event}]`, data);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
logReceive(event, data) {
|
|
953
|
+
const options = this.config.debug;
|
|
954
|
+
const shouldLog = typeof options === "object" ? options.logReceive ?? true : Boolean(options);
|
|
955
|
+
if (shouldLog) {
|
|
956
|
+
this.logger.debug(`\u2190 RECV [${event}]`, data);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function createController(config) {
|
|
961
|
+
const controller = new ControllerImpl(config ?? {});
|
|
962
|
+
const promise = controller.initialize().then(() => controller);
|
|
963
|
+
promise.instance = controller;
|
|
964
|
+
return promise;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
class DirectTransport {
|
|
968
|
+
constructor(socket) {
|
|
969
|
+
this.socket = socket;
|
|
970
|
+
}
|
|
971
|
+
emit(event, ...args) {
|
|
972
|
+
this.socket.emit(event, ...args);
|
|
973
|
+
}
|
|
974
|
+
on(event, handler) {
|
|
975
|
+
this.socket.on(event, handler);
|
|
976
|
+
}
|
|
977
|
+
off(event, handler) {
|
|
978
|
+
if (handler) {
|
|
979
|
+
this.socket.off(event, handler);
|
|
980
|
+
} else {
|
|
981
|
+
this.socket.off(event);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
690
984
|
}
|
|
691
985
|
|
|
692
986
|
const SMORE_EVENTS = {
|
|
@@ -714,24 +1008,267 @@
|
|
|
714
1008
|
return event.startsWith("smore:");
|
|
715
1009
|
}
|
|
716
1010
|
|
|
1011
|
+
function createMockScreen(options = {}) {
|
|
1012
|
+
const {
|
|
1013
|
+
roomCode = "TEST",
|
|
1014
|
+
controllers: initialControllers = [],
|
|
1015
|
+
autoReady = true
|
|
1016
|
+
} = options;
|
|
1017
|
+
let _controllers = [...initialControllers];
|
|
1018
|
+
let _isReady = false;
|
|
1019
|
+
let _isDestroyed = false;
|
|
1020
|
+
let _leaderIndex = initialControllers[0]?.playerIndex ?? -1;
|
|
1021
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
1022
|
+
const broadcasts = [];
|
|
1023
|
+
const sends = [];
|
|
1024
|
+
const screen = {
|
|
1025
|
+
// === Properties ===
|
|
1026
|
+
get controllers() {
|
|
1027
|
+
return [..._controllers];
|
|
1028
|
+
},
|
|
1029
|
+
get roomCode() {
|
|
1030
|
+
return roomCode;
|
|
1031
|
+
},
|
|
1032
|
+
get leaderIndex() {
|
|
1033
|
+
return _leaderIndex;
|
|
1034
|
+
},
|
|
1035
|
+
get isReady() {
|
|
1036
|
+
return _isReady;
|
|
1037
|
+
},
|
|
1038
|
+
get isDestroyed() {
|
|
1039
|
+
return _isDestroyed;
|
|
1040
|
+
},
|
|
1041
|
+
// === Communication Methods ===
|
|
1042
|
+
broadcast(event, data) {
|
|
1043
|
+
if (_isDestroyed) {
|
|
1044
|
+
throw new Error("Cannot broadcast: screen is destroyed");
|
|
1045
|
+
}
|
|
1046
|
+
broadcasts.push({ event, data });
|
|
1047
|
+
},
|
|
1048
|
+
broadcastRaw(event, data) {
|
|
1049
|
+
if (_isDestroyed) {
|
|
1050
|
+
throw new Error("Cannot broadcast: screen is destroyed");
|
|
1051
|
+
}
|
|
1052
|
+
broadcasts.push({ event, data });
|
|
1053
|
+
},
|
|
1054
|
+
sendToController(playerIndex, event, data) {
|
|
1055
|
+
if (_isDestroyed) {
|
|
1056
|
+
throw new Error("Cannot send: screen is destroyed");
|
|
1057
|
+
}
|
|
1058
|
+
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
1059
|
+
throw new Error(`Invalid player index: ${playerIndex}`);
|
|
1060
|
+
}
|
|
1061
|
+
sends.push({ playerIndex, event, data });
|
|
1062
|
+
},
|
|
1063
|
+
sendToControllerRaw(playerIndex, event, data) {
|
|
1064
|
+
if (_isDestroyed) {
|
|
1065
|
+
throw new Error("Cannot send: screen is destroyed");
|
|
1066
|
+
}
|
|
1067
|
+
if (!_controllers.some((c) => c.playerIndex === playerIndex)) {
|
|
1068
|
+
throw new Error(`Invalid player index: ${playerIndex}`);
|
|
1069
|
+
}
|
|
1070
|
+
sends.push({ playerIndex, event, data });
|
|
1071
|
+
},
|
|
1072
|
+
// === Game Lifecycle ===
|
|
1073
|
+
gameOver(results) {
|
|
1074
|
+
screen.broadcastRaw("game-over", results);
|
|
1075
|
+
},
|
|
1076
|
+
// === Event Subscription ===
|
|
1077
|
+
on(event, handler) {
|
|
1078
|
+
const eventStr = event;
|
|
1079
|
+
if (!listeners.has(eventStr)) {
|
|
1080
|
+
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
1081
|
+
}
|
|
1082
|
+
listeners.get(eventStr).add(handler);
|
|
1083
|
+
return () => {
|
|
1084
|
+
listeners.get(eventStr)?.delete(handler);
|
|
1085
|
+
};
|
|
1086
|
+
},
|
|
1087
|
+
once(event, handler) {
|
|
1088
|
+
const wrapper = (playerIndex, data) => {
|
|
1089
|
+
handler(playerIndex, data);
|
|
1090
|
+
screen.off(event, wrapper);
|
|
1091
|
+
};
|
|
1092
|
+
return screen.on(event, wrapper);
|
|
1093
|
+
},
|
|
1094
|
+
off(event, handler) {
|
|
1095
|
+
const eventStr = event;
|
|
1096
|
+
if (!handler) {
|
|
1097
|
+
listeners.delete(eventStr);
|
|
1098
|
+
} else {
|
|
1099
|
+
listeners.get(eventStr)?.delete(handler);
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
// === Utilities ===
|
|
1103
|
+
getController(playerIndex) {
|
|
1104
|
+
return _controllers.find((c) => c.playerIndex === playerIndex);
|
|
1105
|
+
},
|
|
1106
|
+
isLeader(playerIndex) {
|
|
1107
|
+
return playerIndex === _leaderIndex;
|
|
1108
|
+
},
|
|
1109
|
+
getControllerCount() {
|
|
1110
|
+
return _controllers.length;
|
|
1111
|
+
},
|
|
1112
|
+
// === Cleanup ===
|
|
1113
|
+
destroy() {
|
|
1114
|
+
_isDestroyed = true;
|
|
1115
|
+
listeners.clear();
|
|
1116
|
+
broadcasts.length = 0;
|
|
1117
|
+
sends.length = 0;
|
|
1118
|
+
},
|
|
1119
|
+
// === Mock-specific methods ===
|
|
1120
|
+
simulateEvent(playerIndex, event, data) {
|
|
1121
|
+
const eventStr = event;
|
|
1122
|
+
const handlers = listeners.get(eventStr);
|
|
1123
|
+
if (handlers) {
|
|
1124
|
+
handlers.forEach((handler) => {
|
|
1125
|
+
handler(playerIndex, data);
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
},
|
|
1129
|
+
simulateControllerJoin(info) {
|
|
1130
|
+
_controllers.push(info);
|
|
1131
|
+
if (_leaderIndex === -1) {
|
|
1132
|
+
_leaderIndex = info.playerIndex;
|
|
1133
|
+
}
|
|
1134
|
+
},
|
|
1135
|
+
simulateControllerLeave(playerIndex) {
|
|
1136
|
+
_controllers = _controllers.filter((c) => c.playerIndex !== playerIndex);
|
|
1137
|
+
if (_leaderIndex === playerIndex) {
|
|
1138
|
+
_leaderIndex = _controllers[0]?.playerIndex ?? -1;
|
|
1139
|
+
}
|
|
1140
|
+
},
|
|
1141
|
+
getBroadcasts() {
|
|
1142
|
+
return [...broadcasts];
|
|
1143
|
+
},
|
|
1144
|
+
getSentToController(playerIndex) {
|
|
1145
|
+
return sends.filter((s) => s.playerIndex === playerIndex).map((s) => ({ event: s.event, data: s.data }));
|
|
1146
|
+
},
|
|
1147
|
+
clearRecordedEvents() {
|
|
1148
|
+
broadcasts.length = 0;
|
|
1149
|
+
sends.length = 0;
|
|
1150
|
+
},
|
|
1151
|
+
triggerReady() {
|
|
1152
|
+
_isReady = true;
|
|
1153
|
+
}
|
|
1154
|
+
};
|
|
1155
|
+
if (autoReady) {
|
|
1156
|
+
setTimeout(() => screen.triggerReady(), 0);
|
|
1157
|
+
}
|
|
1158
|
+
return screen;
|
|
1159
|
+
}
|
|
1160
|
+
function createMockController(options = {}) {
|
|
1161
|
+
const {
|
|
1162
|
+
roomCode = "TEST",
|
|
1163
|
+
myIndex = 0,
|
|
1164
|
+
isLeader: initialIsLeader = false,
|
|
1165
|
+
autoReady = true
|
|
1166
|
+
} = options;
|
|
1167
|
+
let _isReady = false;
|
|
1168
|
+
let _isDestroyed = false;
|
|
1169
|
+
let _isLeader = initialIsLeader;
|
|
1170
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
1171
|
+
const sentEvents = [];
|
|
1172
|
+
const controller = {
|
|
1173
|
+
// === Properties ===
|
|
1174
|
+
get myIndex() {
|
|
1175
|
+
return myIndex;
|
|
1176
|
+
},
|
|
1177
|
+
get isLeader() {
|
|
1178
|
+
return _isLeader;
|
|
1179
|
+
},
|
|
1180
|
+
get roomCode() {
|
|
1181
|
+
return roomCode;
|
|
1182
|
+
},
|
|
1183
|
+
get isReady() {
|
|
1184
|
+
return _isReady;
|
|
1185
|
+
},
|
|
1186
|
+
get isDestroyed() {
|
|
1187
|
+
return _isDestroyed;
|
|
1188
|
+
},
|
|
1189
|
+
// === Communication Methods ===
|
|
1190
|
+
send(event, data) {
|
|
1191
|
+
if (_isDestroyed) {
|
|
1192
|
+
throw new Error("Cannot send: controller is destroyed");
|
|
1193
|
+
}
|
|
1194
|
+
sentEvents.push({ event, data });
|
|
1195
|
+
},
|
|
1196
|
+
sendRaw(event, data) {
|
|
1197
|
+
if (_isDestroyed) {
|
|
1198
|
+
throw new Error("Cannot send: controller is destroyed");
|
|
1199
|
+
}
|
|
1200
|
+
sentEvents.push({ event, data });
|
|
1201
|
+
},
|
|
1202
|
+
// === Event Subscription ===
|
|
1203
|
+
on(event, handler) {
|
|
1204
|
+
const eventStr = event;
|
|
1205
|
+
if (!listeners.has(eventStr)) {
|
|
1206
|
+
listeners.set(eventStr, /* @__PURE__ */ new Set());
|
|
1207
|
+
}
|
|
1208
|
+
listeners.get(eventStr).add(handler);
|
|
1209
|
+
return () => {
|
|
1210
|
+
listeners.get(eventStr)?.delete(handler);
|
|
1211
|
+
};
|
|
1212
|
+
},
|
|
1213
|
+
once(event, handler) {
|
|
1214
|
+
const wrapper = (data) => {
|
|
1215
|
+
handler(data);
|
|
1216
|
+
controller.off(event, wrapper);
|
|
1217
|
+
};
|
|
1218
|
+
return controller.on(event, wrapper);
|
|
1219
|
+
},
|
|
1220
|
+
off(event, handler) {
|
|
1221
|
+
const eventStr = event;
|
|
1222
|
+
if (!handler) {
|
|
1223
|
+
listeners.delete(eventStr);
|
|
1224
|
+
} else {
|
|
1225
|
+
listeners.get(eventStr)?.delete(handler);
|
|
1226
|
+
}
|
|
1227
|
+
},
|
|
1228
|
+
// === Cleanup ===
|
|
1229
|
+
destroy() {
|
|
1230
|
+
_isDestroyed = true;
|
|
1231
|
+
listeners.clear();
|
|
1232
|
+
sentEvents.length = 0;
|
|
1233
|
+
},
|
|
1234
|
+
// === Mock-specific methods ===
|
|
1235
|
+
simulateEvent(event, data) {
|
|
1236
|
+
const eventStr = event;
|
|
1237
|
+
const handlers = listeners.get(eventStr);
|
|
1238
|
+
if (handlers) {
|
|
1239
|
+
handlers.forEach((handler) => {
|
|
1240
|
+
handler(data);
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
},
|
|
1244
|
+
getSentEvents() {
|
|
1245
|
+
return [...sentEvents];
|
|
1246
|
+
},
|
|
1247
|
+
clearRecordedEvents() {
|
|
1248
|
+
sentEvents.length = 0;
|
|
1249
|
+
},
|
|
1250
|
+
triggerReady() {
|
|
1251
|
+
_isReady = true;
|
|
1252
|
+
},
|
|
1253
|
+
setLeader(isLeader) {
|
|
1254
|
+
_isLeader = isLeader;
|
|
1255
|
+
}
|
|
1256
|
+
};
|
|
1257
|
+
if (autoReady) {
|
|
1258
|
+
setTimeout(() => controller.triggerReady(), 0);
|
|
1259
|
+
}
|
|
1260
|
+
return controller;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
717
1263
|
exports.DirectTransport = DirectTransport;
|
|
718
|
-
exports.DirectionPad = DirectionPad;
|
|
719
|
-
exports.HoldButton = HoldButton;
|
|
720
|
-
exports.HostRoomProvider = HostRoomProvider;
|
|
721
|
-
exports.PlayerRoomProvider = PlayerRoomProvider;
|
|
722
1264
|
exports.PostMessageTransport = PostMessageTransport;
|
|
723
1265
|
exports.SMORE_EVENTS = SMORE_EVENTS;
|
|
724
|
-
exports.
|
|
725
|
-
exports.
|
|
726
|
-
exports.
|
|
1266
|
+
exports.SmoreSDKError = SmoreSDKError$1;
|
|
1267
|
+
exports.createController = createController;
|
|
1268
|
+
exports.createMockController = createMockController;
|
|
1269
|
+
exports.createMockScreen = createMockScreen;
|
|
1270
|
+
exports.createScreen = createScreen;
|
|
727
1271
|
exports.isSystemEvent = isSystemEvent;
|
|
728
|
-
exports.useExternalGames = useExternalGames;
|
|
729
|
-
exports.useGameHost = useGameHost;
|
|
730
|
-
exports.useGamePlayer = useGamePlayer;
|
|
731
|
-
exports.useHostRoom = useHostRoom;
|
|
732
|
-
exports.usePlayerRoom = usePlayerRoom;
|
|
733
|
-
exports.useRoom = useRoom;
|
|
734
|
-
exports.useTransport = useTransport;
|
|
735
1272
|
exports.validateUserEvent = validateUserEvent;
|
|
736
1273
|
|
|
737
1274
|
}));
|