kodenique-game-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -0
  3. package/dist/GameContext.d.ts +4 -0
  4. package/dist/GameContext.js +327 -0
  5. package/dist/GameDebug.d.ts +5 -0
  6. package/dist/GameDebug.js +172 -0
  7. package/dist/GamePlayer.d.ts +26 -0
  8. package/dist/GamePlayer.js +54 -0
  9. package/dist/SimplePlayer.d.ts +11 -0
  10. package/dist/SimplePlayer.js +41 -0
  11. package/dist/components/GamePlayerOverlays.d.ts +29 -0
  12. package/dist/components/GamePlayerOverlays.js +467 -0
  13. package/dist/components/GamePlayerVideo.d.ts +7 -0
  14. package/dist/components/GamePlayerVideo.js +86 -0
  15. package/dist/components/index.d.ts +2 -0
  16. package/dist/components/index.js +2 -0
  17. package/dist/config.d.ts +50 -0
  18. package/dist/config.js +46 -0
  19. package/dist/contexts/GameStreamContext.d.ts +24 -0
  20. package/dist/contexts/GameStreamContext.js +170 -0
  21. package/dist/examples/GameStreamExample.d.ts +26 -0
  22. package/dist/examples/GameStreamExample.js +92 -0
  23. package/dist/examples/SimpleAutoSubscribe.d.ts +9 -0
  24. package/dist/examples/SimpleAutoSubscribe.js +29 -0
  25. package/dist/hooks/index.d.ts +2 -0
  26. package/dist/hooks/index.js +1 -0
  27. package/dist/hooks/useGameStream.d.ts +29 -0
  28. package/dist/hooks/useGameStream.js +78 -0
  29. package/dist/hooks/useWebRTC.d.ts +21 -0
  30. package/dist/hooks/useWebRTC.js +555 -0
  31. package/dist/index.d.ts +13 -0
  32. package/dist/index.js +12 -0
  33. package/dist/lib/pusher.d.ts +50 -0
  34. package/dist/lib/pusher.js +137 -0
  35. package/dist/types.d.ts +87 -0
  36. package/dist/types.js +1 -0
  37. package/dist/useGames.d.ts +2 -0
  38. package/dist/useGames.js +73 -0
  39. package/package.json +66 -0
@@ -0,0 +1,467 @@
1
+ import React from "react";
2
+ export const NetworkIndicator = ({ networkQuality, latency, bitrate, }) => {
3
+ const qualityLabel = {
4
+ good: "Excellent",
5
+ fair: "Fair",
6
+ poor: "Poor",
7
+ bad: "Bad",
8
+ }[networkQuality];
9
+ return (React.createElement("div", { style: {
10
+ position: "absolute",
11
+ top: "15px",
12
+ right: "15px",
13
+ display: "flex",
14
+ alignItems: "center",
15
+ gap: "8px",
16
+ background: "rgba(0, 0, 0, 0.7)",
17
+ padding: "8px 12px",
18
+ borderRadius: "20px",
19
+ fontSize: "12px",
20
+ color: "#fff",
21
+ zIndex: 5,
22
+ } },
23
+ React.createElement("div", { style: {
24
+ display: "flex",
25
+ alignItems: "flex-end",
26
+ gap: "2px",
27
+ height: "16px",
28
+ } }, [4, 8, 12, 16].map((height, i) => (React.createElement("div", { key: i, style: {
29
+ width: "4px",
30
+ height: `${height}px`,
31
+ background: networkQuality === "good"
32
+ ? "#4ade80"
33
+ : networkQuality === "fair" && i < 3
34
+ ? "#fbbf24"
35
+ : networkQuality === "poor" && i < 2
36
+ ? "#ff6b6b"
37
+ : networkQuality === "bad" && i < 1
38
+ ? "#ff6b6b"
39
+ : "#666",
40
+ borderRadius: "1px",
41
+ transition: "background 0.3s",
42
+ } })))),
43
+ React.createElement("span", null, qualityLabel),
44
+ latency > 0 && React.createElement("span", null,
45
+ "~",
46
+ latency,
47
+ "ms"),
48
+ bitrate > 0 && React.createElement("span", null,
49
+ bitrate,
50
+ "kbps")));
51
+ };
52
+ export const ErrorOverlay = ({ errorMessage, onRetry, }) => {
53
+ return (React.createElement("div", { style: {
54
+ position: "absolute",
55
+ top: 0,
56
+ left: 0,
57
+ right: 0,
58
+ bottom: 0,
59
+ display: "flex",
60
+ flexDirection: "column",
61
+ alignItems: "center",
62
+ justifyContent: "center",
63
+ backgroundColor: "rgba(0, 0, 0, 0.9)",
64
+ color: "#fff",
65
+ zIndex: 10,
66
+ cursor: "pointer",
67
+ }, onClick: onRetry },
68
+ React.createElement("div", { style: { fontSize: "48px", marginBottom: "16px" } }, "\uD83D\uDCE1"),
69
+ React.createElement("div", { style: {
70
+ fontSize: "20px",
71
+ fontWeight: "bold",
72
+ marginBottom: "8px",
73
+ } }, "Connection Issue"),
74
+ React.createElement("div", { style: { fontSize: "14px", opacity: 0.7, marginBottom: "20px" } }, errorMessage || "Unable to connect to stream"),
75
+ React.createElement("div", { style: {
76
+ padding: "10px 20px",
77
+ backgroundColor: "#00d4ff",
78
+ color: "#000",
79
+ borderRadius: "5px",
80
+ fontWeight: "bold",
81
+ fontSize: "16px",
82
+ } }, "Click to Retry")));
83
+ };
84
+ export const LoadingOverlay = () => {
85
+ return (React.createElement("div", { style: {
86
+ position: "absolute",
87
+ top: 0,
88
+ left: 0,
89
+ right: 0,
90
+ bottom: 0,
91
+ display: "flex",
92
+ flexDirection: "column",
93
+ alignItems: "center",
94
+ justifyContent: "center",
95
+ backgroundColor: "rgba(0, 0, 0, 0.7)",
96
+ color: "#fff",
97
+ zIndex: 10,
98
+ } },
99
+ React.createElement("div", { style: {
100
+ width: "60px",
101
+ height: "60px",
102
+ border: "4px solid rgba(255, 255, 255, 0.2)",
103
+ borderTopColor: "#00d4ff",
104
+ borderRadius: "50%",
105
+ animation: "spin 1s linear infinite",
106
+ } }),
107
+ React.createElement("div", { style: { marginTop: "15px", fontSize: "18px" } }, "Connecting..."),
108
+ React.createElement("div", { style: { marginTop: "5px", fontSize: "14px", opacity: 0.7 } }, "Establishing WebRTC connection"),
109
+ React.createElement("style", null, `
110
+ @keyframes spin {
111
+ to { transform: rotate(360deg); }
112
+ }
113
+ `)));
114
+ };
115
+ export const RoundWinnerOverlay = ({ round, game, onComplete, }) => {
116
+ const [show, setShow] = React.useState(true);
117
+ React.useEffect(() => {
118
+ // Auto-hide after 5 seconds
119
+ const timer = setTimeout(() => {
120
+ setShow(false);
121
+ setTimeout(onComplete, 500); // Wait for fade out
122
+ }, 5000);
123
+ return () => clearTimeout(timer);
124
+ }, [onComplete]);
125
+ if (!show)
126
+ return null;
127
+ // Determine winner
128
+ const isDraw = round.result === "draw";
129
+ const winnerTeam = game.teams.find(t => t.id === round.winner_team_id);
130
+ const winnerName = (winnerTeam === null || winnerTeam === void 0 ? void 0 : winnerTeam.team_name) || "Unknown";
131
+ const winnerColor = (winnerTeam === null || winnerTeam === void 0 ? void 0 : winnerTeam.team_color) || "#FFF";
132
+ return (React.createElement("div", { style: {
133
+ position: "absolute",
134
+ top: 0,
135
+ left: 0,
136
+ right: 0,
137
+ bottom: 0,
138
+ display: "flex",
139
+ flexDirection: "column",
140
+ alignItems: "center",
141
+ justifyContent: "center",
142
+ backgroundColor: "rgba(0, 0, 0, 0.9)",
143
+ zIndex: 100,
144
+ animation: "fadeIn 0.5s ease-in",
145
+ } },
146
+ React.createElement("div", { style: {
147
+ fontSize: "80px",
148
+ marginBottom: "20px",
149
+ animation: "bounce 1s ease infinite",
150
+ } }, isDraw ? "🀝" : "πŸ†"),
151
+ React.createElement("div", { style: {
152
+ fontSize: "24px",
153
+ fontWeight: "600",
154
+ color: "#94a3b8",
155
+ marginBottom: "10px",
156
+ textTransform: "uppercase",
157
+ letterSpacing: "2px",
158
+ } },
159
+ "Round ",
160
+ round.round_number,
161
+ " Finished"),
162
+ React.createElement("div", { style: {
163
+ fontSize: "48px",
164
+ fontWeight: "bold",
165
+ color: isDraw ? "#fbbf24" : winnerColor,
166
+ marginBottom: "20px",
167
+ textShadow: `0 4px 20px ${isDraw ? "#fbbf24" : winnerColor}80`,
168
+ animation: "pulse 2s ease infinite",
169
+ } }, isDraw ? "DRAW!" : `${winnerName} WINS!`),
170
+ React.createElement("div", { style: {
171
+ fontSize: "32px",
172
+ fontWeight: "600",
173
+ color: "#fff",
174
+ display: "flex",
175
+ gap: "20px",
176
+ alignItems: "center",
177
+ } },
178
+ React.createElement("span", { style: { color: game.teams[0].team_color } },
179
+ game.teams[0].team_name,
180
+ ": ",
181
+ round.red_team_score),
182
+ React.createElement("span", { style: { opacity: 0.5 } }, "-"),
183
+ React.createElement("span", { style: { color: game.teams[1].team_color } },
184
+ game.teams[1].team_name,
185
+ ": ",
186
+ round.blue_team_score)),
187
+ !isDraw && (React.createElement("div", { style: {
188
+ position: "absolute",
189
+ top: "50%",
190
+ left: "50%",
191
+ transform: "translate(-50%, -50%)",
192
+ pointerEvents: "none",
193
+ } }, [...Array(20)].map((_, i) => (React.createElement("div", { key: i, style: {
194
+ position: "absolute",
195
+ width: "10px",
196
+ height: "10px",
197
+ backgroundColor: ["#fbbf24", "#f59e0b", winnerColor, "#4ade80"][i % 4],
198
+ borderRadius: "50%",
199
+ animation: `confetti${i % 4} 3s ease-out infinite`,
200
+ left: `${Math.random() * 100 - 50}px`,
201
+ top: `${Math.random() * 100 - 50}px`,
202
+ } }))))),
203
+ React.createElement("style", null, `
204
+ @keyframes fadeIn {
205
+ from { opacity: 0; }
206
+ to { opacity: 1; }
207
+ }
208
+
209
+ @keyframes bounce {
210
+ 0%, 100% { transform: translateY(0); }
211
+ 50% { transform: translateY(-20px); }
212
+ }
213
+
214
+ @keyframes pulse {
215
+ 0%, 100% {
216
+ opacity: 1;
217
+ transform: scale(1);
218
+ }
219
+ 50% {
220
+ opacity: 0.8;
221
+ transform: scale(1.05);
222
+ }
223
+ }
224
+
225
+ @keyframes confetti0 {
226
+ 0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
227
+ 100% { transform: translate(${Math.random() * 400 - 200}px, ${Math.random() * 400 + 200}px) rotate(720deg); opacity: 0; }
228
+ }
229
+
230
+ @keyframes confetti1 {
231
+ 0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
232
+ 100% { transform: translate(${Math.random() * 400 - 200}px, ${Math.random() * 400 + 200}px) rotate(360deg); opacity: 0; }
233
+ }
234
+
235
+ @keyframes confetti2 {
236
+ 0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
237
+ 100% { transform: translate(${Math.random() * 400 - 200}px, ${Math.random() * 400 + 200}px) rotate(540deg); opacity: 0; }
238
+ }
239
+
240
+ @keyframes confetti3 {
241
+ 0% { transform: translate(0, 0) rotate(0deg); opacity: 1; }
242
+ 100% { transform: translate(${Math.random() * 400 - 200}px, ${Math.random() * 400 + 200}px) rotate(900deg); opacity: 0; }
243
+ }
244
+ `)));
245
+ };
246
+ export const ScoreOverlay = ({ game, gameTitle, showGameTitle = true, showRound = true, showWinnerAnimation = true, // Default: show animations
247
+ }) => {
248
+ const [showWinner, setShowWinner] = React.useState(false);
249
+ const [finishedRound, setFinishedRound] = React.useState(null);
250
+ const prevRoundsRef = React.useRef(game.rounds);
251
+ const isInitialMount = React.useRef(true);
252
+ const shownAnimationRoundIds = React.useRef(new Set());
253
+ // Detect when a round becomes finished (only show for NEW finishes, not on initial load!)
254
+ React.useEffect(() => {
255
+ // Skip animation detection if disabled
256
+ if (!showWinnerAnimation) {
257
+ return;
258
+ }
259
+ const prevRounds = prevRoundsRef.current;
260
+ const currentRounds = game.rounds;
261
+ // Skip on initial mount - mark all existing finished rounds as already shown
262
+ if (isInitialMount.current) {
263
+ isInitialMount.current = false;
264
+ prevRoundsRef.current = currentRounds;
265
+ // Mark all already-finished rounds so we don't show animations for them
266
+ if (currentRounds) {
267
+ currentRounds.forEach((round) => {
268
+ if (round.status === "finished") {
269
+ shownAnimationRoundIds.current.add(round.id);
270
+ }
271
+ });
272
+ }
273
+ return;
274
+ }
275
+ if (currentRounds && prevRounds) {
276
+ // Check if a new round was created - if so, hide any active animation
277
+ const newRoundCreated = currentRounds.find((round) => {
278
+ const prevRound = prevRounds.find((r) => r.id === round.id);
279
+ return !prevRound;
280
+ });
281
+ if (newRoundCreated) {
282
+ setShowWinner(false);
283
+ setFinishedRound(null);
284
+ if (newRoundCreated.status === "finished") {
285
+ shownAnimationRoundIds.current.add(newRoundCreated.id);
286
+ }
287
+ }
288
+ // Find newly finished round
289
+ const newlyFinished = currentRounds.find((round) => {
290
+ const prevRound = prevRounds.find((r) => r.id === round.id);
291
+ if (!prevRound)
292
+ return false;
293
+ const wsAction = round._wsAction;
294
+ const prevWsAction = prevRound._wsAction;
295
+ // Show animation when round transitions to finished status
296
+ const isFinishAction = wsAction === "finished" || wsAction === "winner_set";
297
+ const wasNotFinishActionBefore = prevWsAction !== "finished" && prevWsAction !== "winner_set";
298
+ const isActuallyFinished = round.status === "finished";
299
+ const wasNotFinishedBefore = prevRound.status !== "finished";
300
+ const hasResult = round.result !== null;
301
+ const notShownYet = !shownAnimationRoundIds.current.has(round.id);
302
+ // Show animation if:
303
+ // 1. Action is explicitly "finished" or "winner_set", OR
304
+ // 2. Status changed to "finished" (for direct updates from backend)
305
+ const shouldShow = (isFinishAction && wasNotFinishActionBefore) ||
306
+ (isActuallyFinished && wasNotFinishedBefore);
307
+ return shouldShow && hasResult && notShownYet;
308
+ });
309
+ if (newlyFinished) {
310
+ shownAnimationRoundIds.current.add(newlyFinished.id);
311
+ setFinishedRound(newlyFinished);
312
+ setShowWinner(true);
313
+ }
314
+ }
315
+ prevRoundsRef.current = currentRounds;
316
+ }, [game.rounds, showWinnerAnimation]);
317
+ // Get current round - use the highest round number regardless of status
318
+ const activeRound = game.rounds && game.rounds.length > 0
319
+ ? game.rounds.reduce((highest, current) => current.round_number > highest.round_number ? current : highest)
320
+ : undefined;
321
+ const currentRoundNumber = activeRound === null || activeRound === void 0 ? void 0 : activeRound.round_number;
322
+ // Use custom title or game name
323
+ const displayTitle = gameTitle || game.game_name;
324
+ // Get team names for "Red vs Blue" display
325
+ const teamNames = game.teams.length === 2
326
+ ? `${game.teams[0].team_name} vs ${game.teams[1].team_name}`
327
+ : game.teams.map((t) => t.team_name).join(" vs ");
328
+ return (React.createElement(React.Fragment, null,
329
+ showWinnerAnimation && showWinner && finishedRound && (React.createElement(RoundWinnerOverlay, { round: finishedRound, game: game, onComplete: () => setShowWinner(false) })),
330
+ React.createElement("div", { style: {
331
+ position: "absolute",
332
+ top: "20px",
333
+ left: "50%",
334
+ transform: "translateX(-50%)",
335
+ display: "flex",
336
+ flexDirection: "column",
337
+ alignItems: "center",
338
+ backgroundColor: "rgba(0, 0, 0, 0.85)",
339
+ padding: "16px 28px",
340
+ borderRadius: "12px",
341
+ zIndex: 5,
342
+ backdropFilter: "blur(10px)",
343
+ border: "1px solid rgba(255, 255, 255, 0.1)",
344
+ minWidth: "400px",
345
+ } },
346
+ (showGameTitle || showRound) && (React.createElement("div", { style: {
347
+ display: "flex",
348
+ alignItems: "center",
349
+ gap: "12px",
350
+ marginBottom: "12px",
351
+ width: "100%",
352
+ justifyContent: "center",
353
+ } },
354
+ showGameTitle && (React.createElement("div", { style: {
355
+ fontSize: "18px",
356
+ fontWeight: "bold",
357
+ color: "#fff",
358
+ letterSpacing: "0.5px",
359
+ } }, displayTitle)),
360
+ showGameTitle && showRound && currentRoundNumber && (React.createElement("div", { style: {
361
+ fontSize: "16px",
362
+ fontWeight: "bold",
363
+ color: "#fff",
364
+ opacity: 0.4,
365
+ } }, "-")),
366
+ showRound && currentRoundNumber && (React.createElement("div", { style: {
367
+ fontSize: "16px",
368
+ fontWeight: "600",
369
+ color: "#00d4ff",
370
+ backgroundColor: "rgba(0, 212, 255, 0.15)",
371
+ padding: "4px 12px",
372
+ borderRadius: "6px",
373
+ textTransform: "uppercase",
374
+ letterSpacing: "0.5px",
375
+ } },
376
+ "Round ",
377
+ currentRoundNumber)),
378
+ showRound && (activeRound === null || activeRound === void 0 ? void 0 : activeRound.status) === "finished" && activeRound.result && (React.createElement("div", { style: {
379
+ fontSize: "12px",
380
+ fontWeight: "700",
381
+ color: activeRound.result === "draw" ? "#fbbf24" : "#4ade80",
382
+ backgroundColor: activeRound.result === "draw" ? "rgba(251, 191, 36, 0.2)" : "rgba(74, 222, 128, 0.2)",
383
+ padding: "4px 10px",
384
+ borderRadius: "6px",
385
+ textTransform: "uppercase",
386
+ display: "flex",
387
+ alignItems: "center",
388
+ gap: "4px",
389
+ } }, activeRound.result === "draw" ? "🀝 DRAW" :
390
+ activeRound.result === "red_win" ? "πŸ† RED WINS" :
391
+ activeRound.result === "blue_win" ? "πŸ† BLUE WINS" : "")),
392
+ game.status === "live" && (React.createElement("div", { style: {
393
+ fontSize: "11px",
394
+ fontWeight: "700",
395
+ color: "#4ade80",
396
+ backgroundColor: "rgba(74, 222, 128, 0.2)",
397
+ padding: "4px 8px",
398
+ borderRadius: "6px",
399
+ textTransform: "uppercase",
400
+ display: "flex",
401
+ alignItems: "center",
402
+ gap: "4px",
403
+ } },
404
+ React.createElement("div", { style: {
405
+ width: "6px",
406
+ height: "6px",
407
+ borderRadius: "50%",
408
+ backgroundColor: "#4ade80",
409
+ animation: "pulse 2s ease-in-out infinite",
410
+ } }),
411
+ "LIVE")))),
412
+ React.createElement("div", { style: {
413
+ fontSize: "14px",
414
+ fontWeight: "600",
415
+ color: "#94a3b8",
416
+ textTransform: "uppercase",
417
+ letterSpacing: "1px",
418
+ marginBottom: "14px",
419
+ } }, teamNames),
420
+ React.createElement("div", { style: {
421
+ display: "flex",
422
+ gap: "24px",
423
+ alignItems: "center",
424
+ justifyContent: "center",
425
+ } }, game.teams.map((team, index) => {
426
+ // Use active round scores if available, otherwise fall back to team scores
427
+ const score = activeRound
428
+ ? index === 0
429
+ ? activeRound.red_team_score
430
+ : activeRound.blue_team_score
431
+ : team.current_score;
432
+ return (React.createElement(React.Fragment, { key: team.id },
433
+ index > 0 && (React.createElement("div", { style: {
434
+ fontSize: "24px",
435
+ fontWeight: "bold",
436
+ color: "#fff",
437
+ opacity: 0.3,
438
+ } }, ":")),
439
+ React.createElement("div", { style: {
440
+ display: "flex",
441
+ alignItems: "center",
442
+ gap: "12px",
443
+ } },
444
+ team.logo_url && (React.createElement("img", { src: team.logo_url, alt: team.team_name, style: {
445
+ width: "40px",
446
+ height: "40px",
447
+ borderRadius: "50%",
448
+ objectFit: "cover",
449
+ border: `3px solid ${team.team_color}`,
450
+ boxShadow: `0 0 12px ${team.team_color}40`,
451
+ } })),
452
+ React.createElement("div", { style: {
453
+ fontSize: "32px",
454
+ fontWeight: "bold",
455
+ color: "#fff",
456
+ textShadow: `0 2px 8px ${team.team_color}40`,
457
+ minWidth: "50px",
458
+ textAlign: "center",
459
+ } }, score))));
460
+ })),
461
+ React.createElement("style", null, `
462
+ @keyframes pulse {
463
+ 0%, 100% { opacity: 1; }
464
+ 50% { opacity: 0.4; }
465
+ }
466
+ `))));
467
+ };
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ interface GamePlayerVideoProps {
3
+ videoRef: React.RefObject<HTMLVideoElement>;
4
+ controls: boolean;
5
+ }
6
+ export declare const GamePlayerVideo: React.FC<GamePlayerVideoProps>;
7
+ export {};
@@ -0,0 +1,86 @@
1
+ import React from "react";
2
+ export const GamePlayerVideo = ({ videoRef, controls, }) => {
3
+ const handleVideoEvent = (eventName, extraInfo) => {
4
+ const video = videoRef.current;
5
+ if (!video)
6
+ return;
7
+ const info = extraInfo || {
8
+ readyState: video.readyState,
9
+ paused: video.paused,
10
+ };
11
+ console.log(`[GamePlayer] ${eventName}`, info);
12
+ };
13
+ return (React.createElement("video", { ref: videoRef, autoPlay: true, muted: true, playsInline: true, controls: controls, preload: "auto", style: {
14
+ width: "100%",
15
+ height: "100%",
16
+ objectFit: "contain",
17
+ display: "block",
18
+ backgroundColor: "#000",
19
+ }, onLoadedMetadata: () => {
20
+ const video = videoRef.current;
21
+ if (video) {
22
+ handleVideoEvent("πŸ“‹ Video metadata loaded", {
23
+ dimensions: `${video.videoWidth}x${video.videoHeight}`,
24
+ });
25
+ if (video.paused) {
26
+ video.play().catch(() => { });
27
+ }
28
+ }
29
+ }, onLoadedData: () => {
30
+ var _a;
31
+ handleVideoEvent("πŸ“¦ Video data loaded - readyState:", {
32
+ readyState: (_a = videoRef.current) === null || _a === void 0 ? void 0 : _a.readyState,
33
+ });
34
+ const video = videoRef.current;
35
+ if (video && video.paused) {
36
+ video.play().catch(() => { });
37
+ }
38
+ }, onCanPlay: () => {
39
+ var _a;
40
+ handleVideoEvent("🟒 Video can play! ReadyState:", {
41
+ readyState: (_a = videoRef.current) === null || _a === void 0 ? void 0 : _a.readyState,
42
+ });
43
+ const video = videoRef.current;
44
+ if (video && video.paused) {
45
+ video.play().catch(() => { });
46
+ }
47
+ }, onCanPlayThrough: () => {
48
+ var _a;
49
+ handleVideoEvent("🟒🟒 Video can play through! ReadyState:", {
50
+ readyState: (_a = videoRef.current) === null || _a === void 0 ? void 0 : _a.readyState,
51
+ });
52
+ const video = videoRef.current;
53
+ if (video && video.paused) {
54
+ video.play().catch(() => { });
55
+ }
56
+ }, onPlaying: () => {
57
+ handleVideoEvent("πŸŽ‰ Video is playing!");
58
+ }, onWaiting: () => {
59
+ handleVideoEvent("⏳ Video is waiting for data...");
60
+ }, onStalled: () => {
61
+ handleVideoEvent("⚠️ Video stalled - may need buffering");
62
+ }, onSuspend: () => {
63
+ handleVideoEvent("⏸️ Video data loading suspended");
64
+ }, onProgress: () => {
65
+ const video = videoRef.current;
66
+ if (video && video.buffered.length > 0) {
67
+ handleVideoEvent("πŸ“Š Buffering progress:", {
68
+ buffered: `${video.buffered.end(0)} seconds`,
69
+ });
70
+ }
71
+ }, onClick: () => {
72
+ console.log("[GamePlayer] Video clicked!");
73
+ const video = videoRef.current;
74
+ if (video) {
75
+ if (video.paused) {
76
+ console.log("[GamePlayer] Video is paused, playing...");
77
+ video
78
+ .play()
79
+ .catch((e) => console.log("[GamePlayer] Play failed on click:", e));
80
+ }
81
+ else {
82
+ console.log("[GamePlayer] Video is already playing");
83
+ }
84
+ }
85
+ } }));
86
+ };
@@ -0,0 +1,2 @@
1
+ export { GamePlayerVideo } from "./GamePlayerVideo";
2
+ export { NetworkIndicator, ErrorOverlay, LoadingOverlay, ScoreOverlay, } from "./GamePlayerOverlays";
@@ -0,0 +1,2 @@
1
+ export { GamePlayerVideo } from "./GamePlayerVideo";
2
+ export { NetworkIndicator, ErrorOverlay, LoadingOverlay, ScoreOverlay, } from "./GamePlayerOverlays";
@@ -0,0 +1,50 @@
1
+ export declare const API_CONFIG: {
2
+ development: {
3
+ api_url: string;
4
+ websocket_url: string;
5
+ streaming: {
6
+ serverUrl: string;
7
+ serverPort: number;
8
+ };
9
+ };
10
+ staging: {
11
+ api_url: string;
12
+ websocket_url: string;
13
+ streaming: {
14
+ serverUrl: string;
15
+ serverPort: number;
16
+ };
17
+ };
18
+ production: {
19
+ api_url: string;
20
+ websocket_url: string;
21
+ streaming: {
22
+ serverUrl: string;
23
+ serverPort: number;
24
+ };
25
+ };
26
+ };
27
+ export declare function getConfig(env?: "development" | "staging" | "production"): {
28
+ api_url: string;
29
+ websocket_url: string;
30
+ streaming: {
31
+ serverUrl: string;
32
+ serverPort: number;
33
+ };
34
+ } | {
35
+ api_url: string;
36
+ websocket_url: string;
37
+ streaming: {
38
+ serverUrl: string;
39
+ serverPort: number;
40
+ };
41
+ } | {
42
+ api_url: string;
43
+ websocket_url: string;
44
+ streaming: {
45
+ serverUrl: string;
46
+ serverPort: number;
47
+ };
48
+ };
49
+ export declare function getWebRTCUrl(serverUrl: string, port: number, streamName: string): string;
50
+ export declare function getWebSocketUrl(env?: "development" | "staging" | "production", token?: string): string;
package/dist/config.js ADDED
@@ -0,0 +1,46 @@
1
+ // API Configuration for Game SDK
2
+ export const API_CONFIG = {
3
+ // Development (local)
4
+ development: {
5
+ api_url: "http://localhost:5001/v1/external/games",
6
+ websocket_url: "ws.kodenique.com",
7
+ streaming: {
8
+ serverUrl: "localhost",
9
+ serverPort: 1985,
10
+ },
11
+ },
12
+ // Staging
13
+ staging: {
14
+ api_url: "https://api-stg.wspo.club/v1/external/games",
15
+ websocket_url: "ws.kodenique.com",
16
+ streaming: {
17
+ serverUrl: "143.198.90.133",
18
+ serverPort: 1985,
19
+ },
20
+ },
21
+ // Production
22
+ production: {
23
+ api_url: "https://api.wspo.club/v1/external/games",
24
+ websocket_url: "ws.kodenique.com",
25
+ streaming: {
26
+ serverUrl: "143.198.90.133",
27
+ serverPort: 1985,
28
+ },
29
+ },
30
+ };
31
+ // Helper to get current environment config
32
+ export function getConfig(env = "development") {
33
+ return API_CONFIG[env];
34
+ }
35
+ // Helper to construct WebRTC URL
36
+ export function getWebRTCUrl(serverUrl, port, streamName) {
37
+ return `http://${serverUrl}:${port}/rtc/v1/whep/?app=live&stream=${streamName}`;
38
+ }
39
+ // Helper to construct WebSocket URL with token
40
+ export function getWebSocketUrl(env = "development", token) {
41
+ const baseUrl = API_CONFIG[env].websocket_url;
42
+ if (token) {
43
+ return `${baseUrl}?token=${token}`;
44
+ }
45
+ return baseUrl;
46
+ }