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,137 @@
1
+ /**
2
+ * Pusher integration for real-time game updates
3
+ * Alternative to WebSocket for easier setup
4
+ *
5
+ * Note: Install pusher-js to use this: npm install pusher-js
6
+ */
7
+ export class GamePusherClient {
8
+ constructor(config) {
9
+ this.config = config;
10
+ this.pusher = null;
11
+ this.channels = new Map();
12
+ }
13
+ /**
14
+ * Initialize Pusher connection
15
+ */
16
+ async connect() {
17
+ if (this.pusher) {
18
+ console.log("[GamePusher] Already connected");
19
+ return;
20
+ }
21
+ console.log("[GamePusher] Connecting to Pusher...");
22
+ // Dynamic import to make pusher-js optional
23
+ try {
24
+ // @ts-ignore - dynamic import
25
+ const PusherModule = await import("pusher-js");
26
+ const PusherConstructor = PusherModule.default || PusherModule;
27
+ this.pusher = new PusherConstructor(this.config.key, {
28
+ cluster: this.config.cluster,
29
+ authEndpoint: this.config.authEndpoint,
30
+ });
31
+ }
32
+ catch (error) {
33
+ console.error("[GamePusher] Pusher not installed. Install with: npm install pusher-js");
34
+ return;
35
+ }
36
+ this.pusher.connection.bind("connected", () => {
37
+ console.log("[GamePusher] Connected to Pusher");
38
+ });
39
+ this.pusher.connection.bind("disconnected", () => {
40
+ console.log("[GamePusher] Disconnected from Pusher");
41
+ });
42
+ this.pusher.connection.bind("error", (error) => {
43
+ console.error("[GamePusher] Connection error:", error);
44
+ });
45
+ }
46
+ /**
47
+ * Subscribe to a game channel
48
+ */
49
+ subscribeToGame(gameId, callbacks) {
50
+ if (!this.pusher) {
51
+ console.error("[GamePusher] Not connected. Call connect() first.");
52
+ return;
53
+ }
54
+ const channelName = `game.${gameId}`;
55
+ if (this.channels.has(channelName)) {
56
+ console.log("[GamePusher] Already subscribed to:", channelName);
57
+ return;
58
+ }
59
+ console.log("[GamePusher] Subscribing to channel:", channelName);
60
+ const channel = this.pusher.subscribe(channelName);
61
+ // Listen for game update events
62
+ if (callbacks.onGameUpdate) {
63
+ channel.bind("game-update", callbacks.onGameUpdate);
64
+ }
65
+ // Listen for score update events
66
+ if (callbacks.onScoreUpdate) {
67
+ channel.bind("score-update", callbacks.onScoreUpdate);
68
+ }
69
+ // Listen for status update events
70
+ if (callbacks.onStatusUpdate) {
71
+ channel.bind("status-update", callbacks.onStatusUpdate);
72
+ }
73
+ // Listen for game ended events
74
+ if (callbacks.onGameEnded) {
75
+ channel.bind("game-ended", callbacks.onGameEnded);
76
+ }
77
+ this.channels.set(channelName, channel);
78
+ }
79
+ /**
80
+ * Unsubscribe from a game channel
81
+ */
82
+ unsubscribeFromGame(gameId) {
83
+ const channelName = `game.${gameId}`;
84
+ if (!this.channels.has(channelName)) {
85
+ console.log("[GamePusher] Not subscribed to:", channelName);
86
+ return;
87
+ }
88
+ console.log("[GamePusher] Unsubscribing from channel:", channelName);
89
+ if (this.pusher) {
90
+ this.pusher.unsubscribe(channelName);
91
+ }
92
+ this.channels.delete(channelName);
93
+ }
94
+ /**
95
+ * Disconnect from Pusher
96
+ */
97
+ disconnect() {
98
+ if (!this.pusher)
99
+ return;
100
+ console.log("[GamePusher] Disconnecting from Pusher");
101
+ // Unsubscribe from all channels
102
+ this.channels.forEach((_, channelName) => {
103
+ var _a;
104
+ (_a = this.pusher) === null || _a === void 0 ? void 0 : _a.unsubscribe(channelName);
105
+ });
106
+ this.channels.clear();
107
+ this.pusher.disconnect();
108
+ this.pusher = null;
109
+ }
110
+ /**
111
+ * Check if connected
112
+ */
113
+ isConnected() {
114
+ var _a;
115
+ return ((_a = this.pusher) === null || _a === void 0 ? void 0 : _a.connection.state) === "connected";
116
+ }
117
+ }
118
+ // Singleton instance
119
+ let pusherClient = null;
120
+ /**
121
+ * Initialize the global Pusher client
122
+ */
123
+ export function initializePusher(config) {
124
+ if (pusherClient) {
125
+ console.warn("[GamePusher] Already initialized");
126
+ return pusherClient;
127
+ }
128
+ pusherClient = new GamePusherClient(config);
129
+ pusherClient.connect();
130
+ return pusherClient;
131
+ }
132
+ /**
133
+ * Get the global Pusher client
134
+ */
135
+ export function getPusherClient() {
136
+ return pusherClient;
137
+ }
@@ -0,0 +1,87 @@
1
+ export interface Team {
2
+ id: string;
3
+ game_id: string;
4
+ team_name: string;
5
+ team_color: string;
6
+ logo_url: string;
7
+ current_score: number;
8
+ created_at: string;
9
+ updated_at: string;
10
+ }
11
+ export interface Round {
12
+ id: string;
13
+ game_id: string;
14
+ round_number: number;
15
+ status: 'active' | 'open' | 'closed' | 'finished' | 'cancelled';
16
+ red_team_score: number;
17
+ blue_team_score: number;
18
+ result: 'red_win' | 'blue_win' | 'draw' | null;
19
+ winner_team_id: string | null;
20
+ started_at: string;
21
+ ended_at: string | null;
22
+ metadata: any;
23
+ created_at: string;
24
+ updated_at: string;
25
+ winnerTeam: {
26
+ id: string;
27
+ team_name: string;
28
+ team_color: string;
29
+ } | null;
30
+ }
31
+ export interface Creator {
32
+ id: string;
33
+ username: string;
34
+ email: string;
35
+ }
36
+ export interface Game {
37
+ id: string;
38
+ game_name: string;
39
+ description: string;
40
+ status: 'active' | 'live' | 'paused' | 'finished' | 'cancelled';
41
+ scheduled_start: string;
42
+ started_at: string | null;
43
+ ended_at: string | null;
44
+ metadata: Record<string, any> | null;
45
+ display_stream_url: string;
46
+ stream_name?: string;
47
+ stream_server?: string;
48
+ created_at: string;
49
+ updated_at: string;
50
+ teams: Team[];
51
+ rounds: Round[];
52
+ creator: Creator;
53
+ }
54
+ export interface GamesApiResponse {
55
+ data: Game[];
56
+ }
57
+ export interface GameProviderProps {
58
+ access_token: string;
59
+ api_url?: string;
60
+ children: React.ReactNode;
61
+ socketToken?: string;
62
+ environment?: 'development' | 'staging' | 'production';
63
+ websocketUrl?: string;
64
+ }
65
+ export interface GameContextType {
66
+ access_token: string;
67
+ api_url: string;
68
+ subscribeToGame: (gameId: string) => void;
69
+ unsubscribeFromGame: (gameId: string) => void;
70
+ getGame: (gameId: string) => Game | undefined;
71
+ updateGame: (game: Game) => void;
72
+ isConnected: boolean;
73
+ subscribedGameIds: string[];
74
+ activeGames: Game[];
75
+ }
76
+ export interface UseGamesOptions {
77
+ access_token?: string;
78
+ api_url?: string;
79
+ }
80
+ export interface UseGamesReturn {
81
+ games: Game[];
82
+ fetchGames: () => Promise<Game[]>;
83
+ getGameById: (gameId: string) => Game | undefined;
84
+ loading: boolean;
85
+ error: Error | null;
86
+ refetch: () => Promise<void>;
87
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import { UseGamesOptions, UseGamesReturn } from "./types";
2
+ export declare const useGames: (options?: UseGamesOptions) => UseGamesReturn;
@@ -0,0 +1,73 @@
1
+ import { useState, useCallback } from "react";
2
+ import { useGameContext } from "./GameContext";
3
+ export const useGames = (options) => {
4
+ const [games, setGames] = useState([]);
5
+ const [loading, setLoading] = useState(false);
6
+ const [error, setError] = useState(null);
7
+ // Try to get context, but allow options to override
8
+ let contextConfig;
9
+ try {
10
+ contextConfig = useGameContext();
11
+ }
12
+ catch (e) {
13
+ // No context available, use options
14
+ contextConfig = null;
15
+ }
16
+ const access_token = (options === null || options === void 0 ? void 0 : options.access_token) || (contextConfig === null || contextConfig === void 0 ? void 0 : contextConfig.access_token);
17
+ const api_url = (options === null || options === void 0 ? void 0 : options.api_url) ||
18
+ (contextConfig === null || contextConfig === void 0 ? void 0 : contextConfig.api_url) ||
19
+ "http://localhost:5001/v1/external/games";
20
+ if (!access_token) {
21
+ throw new Error("access_token is required. Provide it via useGames options or GameProvider.");
22
+ }
23
+ // Fetch all games that the access token has permission to view
24
+ const fetchGames = useCallback(async () => {
25
+ setLoading(true);
26
+ setError(null);
27
+ // Debug logging
28
+ console.log("[useGames] Fetching from:", api_url);
29
+ console.log("[useGames] Access token:", access_token ? "Present" : "Missing");
30
+ try {
31
+ const response = await fetch(api_url, {
32
+ method: "GET",
33
+ headers: {
34
+ Authorization: `Bearer ${access_token}`,
35
+ "Content-Type": "application/json",
36
+ },
37
+ });
38
+ if (!response.ok) {
39
+ if (response.status === 401) {
40
+ throw new Error("Unauthorized - Invalid or expired access token");
41
+ }
42
+ throw new Error(`Failed to fetch games: ${response.statusText}`);
43
+ }
44
+ const result = await response.json();
45
+ setGames(result.data);
46
+ return result.data;
47
+ }
48
+ catch (err) {
49
+ const error = err instanceof Error ? err : new Error("Unknown error occurred");
50
+ setError(error);
51
+ throw error;
52
+ }
53
+ finally {
54
+ setLoading(false);
55
+ }
56
+ }, [access_token, api_url]);
57
+ // Helper function to get a specific game by ID from the cached games
58
+ const getGameById = useCallback((gameId) => {
59
+ return games.find((game) => game.id === gameId);
60
+ }, [games]);
61
+ // Refetch function for manual refresh
62
+ const refetch = useCallback(async () => {
63
+ await fetchGames();
64
+ }, [fetchGames]);
65
+ return {
66
+ games,
67
+ fetchGames,
68
+ getGameById,
69
+ loading,
70
+ error,
71
+ refetch,
72
+ };
73
+ };
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "kodenique-game-sdk",
3
+ "version": "1.0.0",
4
+ "description": "React SDK for real-time game streaming with WebSocket updates and WebRTC video player",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.esm.js",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "README.md",
18
+ "LICENSE"
19
+ ],
20
+ "scripts": {
21
+ "build": "tsc",
22
+ "dev": "tsc --watch",
23
+ "test": "jest",
24
+ "prepublishOnly": "npm run build",
25
+ "clean": "rm -rf dist"
26
+ },
27
+ "keywords": [
28
+ "react",
29
+ "game",
30
+ "sdk",
31
+ "websocket",
32
+ "webrtc",
33
+ "streaming",
34
+ "real-time",
35
+ "video-player",
36
+ "socketcluster",
37
+ "whep",
38
+ "live-streaming"
39
+ ],
40
+ "author": "Kodenique",
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://github.com/kodenique/game-sdk.git"
45
+ },
46
+ "bugs": {
47
+ "url": "https://github.com/kodenique/game-sdk/issues"
48
+ },
49
+ "homepage": "https://github.com/kodenique/game-sdk#readme",
50
+ "peerDependencies": {
51
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0",
52
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^25.0.10",
56
+ "@types/react": "^18.0.0",
57
+ "@types/react-dom": "^18.0.0",
58
+ "typescript": "^5.0.0"
59
+ },
60
+ "dependencies": {
61
+ "@types/pako": "^2.0.4",
62
+ "axios": "^1.7.9",
63
+ "pako": "^2.1.0",
64
+ "socketcluster-wrapper-client": "^1.1.8"
65
+ }
66
+ }