playe-developer-sdk 1.0.10 → 1.0.13
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 +628 -628
- package/dist/{index.cjs.js → index.cjs} +116 -135
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +22 -24
- package/dist/{index.esm.js → index.js} +116 -135
- package/dist/index.js.map +1 -0
- package/dist/playe-sdk.umd.js +115 -134
- package/dist/playe-sdk.umd.js.map +1 -1
- package/dist/playe-sdk.umd.min.js +1 -1
- package/dist/playe-sdk.umd.min.js.map +1 -1
- package/package.json +11 -10
- package/dist/index.cjs.js.map +0 -1
- package/dist/index.esm.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,629 +1,629 @@
|
|
|
1
|
-
# 🎮 Playe Developer SDK
|
|
2
|
-
|
|
3
|
-
[](https://npmjs.com/package/playe-developer-sdk)
|
|
4
|
-
[](https://github.com/playe/playe/blob/main/LICENSE)
|
|
5
|
-
[](https://bundlephobia.com/package/playe-developer-sdk)
|
|
6
|
-
[](https://www.typescriptlang.org/)
|
|
7
|
-
|
|
8
|
-
A browser-based SDK for integrating games with the Playe platform. Provides game session management, campaign integration, leaderboards, and integrity verification.
|
|
9
|
-
|
|
10
|
-
## ✨ Features
|
|
11
|
-
|
|
12
|
-
- 🎮 **Game Session Management** - Initialize, start, update, and complete game sessions
|
|
13
|
-
- 🛡️ **Integrity Verification** - Built-in session integrity with cryptographic hashing
|
|
14
|
-
- 🏆 **Campaign Integration** - Access campaign information, leaderboards, and player attempts
|
|
15
|
-
- 🔄 **Anti-Cheat Protection** - Advanced validation, heartbeat mechanisms, and session monitoring
|
|
16
|
-
- 📱 **Device Detection** - Automatic device information and fingerprinting
|
|
17
|
-
- 🔧 **Developer Tools** - Test game sessions and debugging utilities
|
|
18
|
-
- 📊 **Real-time Updates** - Progress tracking and live leaderboards
|
|
19
|
-
- ⚡ **Browser Optimized** - Lightweight and fast for web games with automatic retry logic
|
|
20
|
-
|
|
21
|
-
## 🚀 Installation
|
|
22
|
-
|
|
23
|
-
### NPM
|
|
24
|
-
|
|
25
|
-
```bash
|
|
26
|
-
npm install playe-developer-sdk
|
|
27
|
-
# or
|
|
28
|
-
yarn add playe-developer-sdk
|
|
29
|
-
# or
|
|
30
|
-
pnpm add playe-developer-sdk
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
### CDN
|
|
34
|
-
|
|
35
|
-
```html
|
|
36
|
-
<script src="https://unpkg.com/playe-developer-sdk@latest/dist/playe-sdk.umd.min.js"></script>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Workspace
|
|
40
|
-
|
|
41
|
-
```json
|
|
42
|
-
{
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"@workspace/playe-developer-sdk": "workspace:*"
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
## 📖 Quick Start
|
|
50
|
-
|
|
51
|
-
### NPM Usage
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
import { createPlayeSDK } from "playe-developer-sdk";
|
|
55
|
-
|
|
56
|
-
// Initialize SDK
|
|
57
|
-
const sdk = createPlayeSDK({
|
|
58
|
-
apiBaseUrl: "https://api.playe.com",
|
|
59
|
-
apiKey: "your-api-key",
|
|
60
|
-
enableDebugMode: true,
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
// Initialize game session (gets sessionId from URL)
|
|
64
|
-
const session = await sdk.initializeGameSession();
|
|
65
|
-
|
|
66
|
-
// Start game
|
|
67
|
-
await sdk.startGameSession({
|
|
68
|
-
difficulty: "normal",
|
|
69
|
-
mode: "classic",
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Update progress
|
|
73
|
-
await sdk.updateGameProgress({
|
|
74
|
-
currentScore: 1500,
|
|
75
|
-
elapsedTime: 120,
|
|
76
|
-
gameState: {
|
|
77
|
-
level: 3,
|
|
78
|
-
lives: 2,
|
|
79
|
-
powerUps: ["speed", "shield"],
|
|
80
|
-
},
|
|
81
|
-
achievements: [
|
|
82
|
-
{
|
|
83
|
-
id: "level-3",
|
|
84
|
-
name: "Level 3 Complete",
|
|
85
|
-
unlockedAt: new Date().toISOString(),
|
|
86
|
-
},
|
|
87
|
-
],
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Complete game
|
|
91
|
-
const result = await sdk.completeGame({
|
|
92
|
-
finalScore: 2500,
|
|
93
|
-
validationChecksum: "client-checksum",
|
|
94
|
-
gameMetrics: {
|
|
95
|
-
totalPlayTime: 300,
|
|
96
|
-
actionsPerformed: 150,
|
|
97
|
-
powerUpsUsed: 5,
|
|
98
|
-
levelsCompleted: 5,
|
|
99
|
-
},
|
|
100
|
-
finalGameState: {
|
|
101
|
-
level: 5,
|
|
102
|
-
finalLives: 1,
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### CDN Usage
|
|
108
|
-
|
|
109
|
-
```html
|
|
110
|
-
<script src="https://unpkg.com/playe-developer-sdk@latest/dist/playe-sdk.umd.min.js"></script>
|
|
111
|
-
<script>
|
|
112
|
-
const sdk = PlayeSDK.createPlayeSDK({
|
|
113
|
-
apiBaseUrl: 'https://api.playe.com',
|
|
114
|
-
apiKey: 'your-api-key',
|
|
115
|
-
enableDebugMode: true,
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
const session = await sdk.initializeGameSession();
|
|
119
|
-
console.log('Session initialized:', session.sessionId);
|
|
120
|
-
</script>
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
## 📋 Table of Contents
|
|
124
|
-
|
|
125
|
-
- [🎮 Playe Developer SDK](#-playe-developer-sdk)
|
|
126
|
-
- [✨ Features](#-features)
|
|
127
|
-
- [🚀 Installation](#-installation)
|
|
128
|
-
- [📖 Quick Start](#-quick-start)
|
|
129
|
-
- [📋 Table of Contents](#-table-of-contents)
|
|
130
|
-
- [🔧 API Reference](#-api-reference)
|
|
131
|
-
- [🔒 Security & Integrity](#-security--integrity)
|
|
132
|
-
- [🧪 Testing & Development](#-testing--development)
|
|
133
|
-
- [🚨 Error Handling](#-error-handling)
|
|
134
|
-
- [📚 Examples](#-examples)
|
|
135
|
-
- [🔄 Migration Guide](#-migration-guide)
|
|
136
|
-
- [🔧 Troubleshooting](#-troubleshooting)
|
|
137
|
-
- [🤝 Contributing](#-contributing)
|
|
138
|
-
- [📄 License](#-license)
|
|
139
|
-
|
|
140
|
-
## 🔧 API Reference
|
|
141
|
-
|
|
142
|
-
### SDK Configuration
|
|
143
|
-
|
|
144
|
-
```typescript
|
|
145
|
-
interface SDKConfig {
|
|
146
|
-
apiBaseUrl: string; // Required: API base URL
|
|
147
|
-
apiKey?: string; // Optional: API key for authentication
|
|
148
|
-
enableDebugMode?: boolean; // Optional: Enable debug logging
|
|
149
|
-
retryAttempts?: number; // Optional: Number of retry attempts (default: 3)
|
|
150
|
-
timeoutMs?: number; // Optional: Request timeout in ms (default: 30000)
|
|
151
|
-
enableOfflineMode?: boolean; // Optional: Enable offline mode (default: false)
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Core Methods
|
|
156
|
-
|
|
157
|
-
#### Game Session Management
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
// Initialize game session (gets sessionId from URL query parameter)
|
|
161
|
-
const session = await sdk.initializeGameSession(): Promise<GameSessionResponse>;
|
|
162
|
-
// Returns: { sessionId, campaignId, gameId, status, campaign, player, createdAt, expiresAt }
|
|
163
|
-
|
|
164
|
-
// Start game session
|
|
165
|
-
await sdk.startGameSession(config?: Record<string, any>): Promise<GameSessionResponse>;
|
|
166
|
-
// Returns: { sessionId, status, startTime }
|
|
167
|
-
|
|
168
|
-
// Update game progress with integrity verification
|
|
169
|
-
await sdk.updateGameProgress({
|
|
170
|
-
currentScore: number;
|
|
171
|
-
gameState?: Record<string, any>; // Automatically hashed for integrity
|
|
172
|
-
elapsedTime: number;
|
|
173
|
-
achievements?: Achievement[];
|
|
174
|
-
clientTimestamp?: string;
|
|
175
|
-
}): Promise<ProgressUpdateResponse>;
|
|
176
|
-
// Returns: { isValid, validationMessage, currentRank, isLeading, campaignStatus, warnings }
|
|
177
|
-
|
|
178
|
-
// Complete game with integrity verification
|
|
179
|
-
await sdk.completeGame({
|
|
180
|
-
finalScore: number;
|
|
181
|
-
validationChecksum: string;
|
|
182
|
-
gameMetrics?: GameMetrics;
|
|
183
|
-
finalGameState?: Record<string, any>; // Automatically hashed for integrity
|
|
184
|
-
}): Promise<GameCompletionResponse>;
|
|
185
|
-
// Returns: { isWinner, prizeAmount, prizeType, finalRank, totalParticipants, winnerDetails, reward, campaignResult }
|
|
186
|
-
|
|
187
|
-
// Abandon game
|
|
188
|
-
await sdk.abandonGame(reason?: string, lastKnownScore?: number): Promise<AbandonGameResponse>;
|
|
189
|
-
// Returns: { success, message, gamepassRefunded, abandonedAt }
|
|
190
|
-
```
|
|
191
|
-
|
|
192
|
-
#### Campaign Information
|
|
193
|
-
|
|
194
|
-
```typescript
|
|
195
|
-
// Get campaign status
|
|
196
|
-
await sdk.getCampaignStatus(campaignId: string, playerId?: string): Promise<CampaignStatusResponse>;
|
|
197
|
-
// Returns: { campaignId, status, campaignStyle, remainingGamepasses, totalParticipants,
|
|
198
|
-
// totalPrizePool, endDate, playerStatus, topPlayers, prizeBreakdown }
|
|
199
|
-
|
|
200
|
-
// Get leaderboard
|
|
201
|
-
await sdk.getLeaderboard(campaignId: string, limit?: number, playerId?: string): Promise<LeaderboardResponse>;
|
|
202
|
-
// Returns: { campaignId, entries, playerRank, lastUpdated, isLive }
|
|
203
|
-
// entries: [{ rank, playerId, displayName, score, achievedAt, isCurrentPlayer, prizeAmount }]
|
|
204
|
-
|
|
205
|
-
// Get player attempts
|
|
206
|
-
await sdk.getPlayerAttempts(campaignId: string, playerId: string): Promise<PlayerAttemptsResponse>;
|
|
207
|
-
// Returns: { playerId, campaignId, totalAttempts, remainingAttempts, attempts, stats }
|
|
208
|
-
// attempts: [{ sessionId, score, playedAt, duration, status, wasWinner }]
|
|
209
|
-
// stats: { bestScore, averageScore, totalPlayTime, firstAttempt, lastAttempt }
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
#### Validation & Anti-Cheat
|
|
213
|
-
|
|
214
|
-
```typescript
|
|
215
|
-
// Validate game session with integrity verification
|
|
216
|
-
await sdk.validateGameSession({
|
|
217
|
-
clientChecksum: string;
|
|
218
|
-
stateSnapshot?: Record<string, unknown>; // Automatically hashed
|
|
219
|
-
timingData?: unknown;
|
|
220
|
-
behaviorMetrics?: unknown;
|
|
221
|
-
}): Promise<ValidationResponse>;
|
|
222
|
-
// Returns: { isValid, confidenceScore, validationIssues, warnings, requiresRevalidation, validatedAt }
|
|
223
|
-
|
|
224
|
-
// Send heartbeat (usually handled automatically)
|
|
225
|
-
await sdk.sendHeartbeat(currentScore?: number): Promise<HeartbeatResponse>;
|
|
226
|
-
// Returns: { acknowledged, serverTimestamp, nextHeartbeatIn, campaignStillActive, message }
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
#### Developer Tools
|
|
230
|
-
|
|
231
|
-
```typescript
|
|
232
|
-
// Create test game session for development
|
|
233
|
-
await sdk.createTestGameSession(testConfig?: Record<string, any>): Promise<TestGameResponse>;
|
|
234
|
-
// Returns: { testSessionId, testScenario, mockCampaign, testData, expiresAt }
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
### Utility Methods
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
// Test SDK functionality
|
|
241
|
-
sdk.test('Custom test message'): void;
|
|
242
|
-
|
|
243
|
-
// Check if running in demo mode
|
|
244
|
-
const isDemoMode = sdk.isDemo(): boolean;
|
|
245
|
-
|
|
246
|
-
// Game lifecycle events
|
|
247
|
-
sdk.gameLoadingStart(): void; // Emit loading start event
|
|
248
|
-
sdk.gameLoadingFinished(): void; // Emit loading finished event
|
|
249
|
-
sdk.gamePlayStop(): void; // Stop heartbeat and emit stop event
|
|
250
|
-
|
|
251
|
-
// Clean up resources
|
|
252
|
-
sdk.destroy(): void; // Clean up session artifacts and stop heartbeat
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Response Types
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
// Game Session Response
|
|
259
|
-
interface GameSessionResponse {
|
|
260
|
-
sessionId: string;
|
|
261
|
-
campaignId: string;
|
|
262
|
-
gameId: string;
|
|
263
|
-
status: string;
|
|
264
|
-
campaign: CampaignInfo;
|
|
265
|
-
player: PlayerInfo;
|
|
266
|
-
createdAt: string;
|
|
267
|
-
expiresAt?: string;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
// Progress Update Response
|
|
271
|
-
interface ProgressUpdateResponse {
|
|
272
|
-
isValid: boolean;
|
|
273
|
-
validationMessage?: string;
|
|
274
|
-
currentRank: number;
|
|
275
|
-
isLeading: boolean;
|
|
276
|
-
campaignStatus?: CampaignStatusInfo;
|
|
277
|
-
warnings?: string[];
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Game Completion Response
|
|
281
|
-
interface GameCompletionResponse {
|
|
282
|
-
isWinner: boolean;
|
|
283
|
-
prizeAmount?: number;
|
|
284
|
-
prizeType?: string;
|
|
285
|
-
finalRank: number;
|
|
286
|
-
totalParticipants: number;
|
|
287
|
-
winnerDetails?: WinnerInfo;
|
|
288
|
-
reward?: RewardInfo;
|
|
289
|
-
campaignResult?: CampaignResultInfo;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Campaign Status Response
|
|
293
|
-
interface CampaignStatusResponse {
|
|
294
|
-
campaignId: string;
|
|
295
|
-
status: string;
|
|
296
|
-
campaignStyle: string;
|
|
297
|
-
remainingGamepasses: number;
|
|
298
|
-
totalParticipants: number;
|
|
299
|
-
totalPrizePool: number;
|
|
300
|
-
endDate?: string;
|
|
301
|
-
playerStatus?: PlayerStatusInfo;
|
|
302
|
-
topPlayers?: TopPlayer[];
|
|
303
|
-
prizeBreakdown?: PrizeBreakdown;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
// Leaderboard Response
|
|
307
|
-
interface LeaderboardResponse {
|
|
308
|
-
campaignId: string;
|
|
309
|
-
entries: LeaderboardEntry[];
|
|
310
|
-
playerRank?: PlayerRankInfo;
|
|
311
|
-
lastUpdated: string;
|
|
312
|
-
isLive: boolean;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Validation Response
|
|
316
|
-
interface ValidationResponse {
|
|
317
|
-
isValid: boolean;
|
|
318
|
-
confidenceScore: number;
|
|
319
|
-
validationIssues: string[];
|
|
320
|
-
warnings: string[];
|
|
321
|
-
requiresRevalidation: boolean;
|
|
322
|
-
validatedAt: string;
|
|
323
|
-
}
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
### Integrity Verification Utilities
|
|
327
|
-
|
|
328
|
-
```typescript
|
|
329
|
-
import { canonicalStringify, sha256Hex } from "playe-developer-sdk";
|
|
330
|
-
|
|
331
|
-
// Canonical JSON stringification (deterministic)
|
|
332
|
-
const canonical = canonicalStringify({ b: 1, a: 2 }); // '{"a":2,"b":1}'
|
|
333
|
-
|
|
334
|
-
// SHA-256 hashing
|
|
335
|
-
const hash = await sha256Hex("data to hash");
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
### Device Utilities
|
|
339
|
-
|
|
340
|
-
```typescript
|
|
341
|
-
import { DeviceUtils } from "@workspace/playe-developer-sdk";
|
|
342
|
-
|
|
343
|
-
// Get device information
|
|
344
|
-
const deviceInfo = DeviceUtils.getDeviceInfo();
|
|
345
|
-
|
|
346
|
-
// Get geolocation
|
|
347
|
-
const location = await DeviceUtils.getGeolocationData();
|
|
348
|
-
|
|
349
|
-
// Generate fingerprint
|
|
350
|
-
const fingerprint = DeviceUtils.generateFingerprint();
|
|
351
|
-
|
|
352
|
-
// Check browser support
|
|
353
|
-
const isSupported = DeviceUtils.isBrowser();
|
|
354
|
-
const features = DeviceUtils.getFeatureSupport();
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
### Error Handling
|
|
358
|
-
|
|
359
|
-
```typescript
|
|
360
|
-
import {
|
|
361
|
-
PlayeSDKError,
|
|
362
|
-
ValidationError,
|
|
363
|
-
NetworkError,
|
|
364
|
-
AuthenticationError,
|
|
365
|
-
GameSessionError,
|
|
366
|
-
} from "@workspace/playe-developer-sdk";
|
|
367
|
-
|
|
368
|
-
try {
|
|
369
|
-
await sdk.initializeGameSession(params);
|
|
370
|
-
} catch (error) {
|
|
371
|
-
if (error instanceof ValidationError) {
|
|
372
|
-
console.error("Validation failed:", error.message);
|
|
373
|
-
} else if (error instanceof NetworkError) {
|
|
374
|
-
console.error("Network error:", error.message);
|
|
375
|
-
} else if (error instanceof AuthenticationError) {
|
|
376
|
-
console.error("Authentication failed:", error.message);
|
|
377
|
-
} else if (error instanceof PlayeSDKError) {
|
|
378
|
-
console.error("SDK error:", error.toJSON());
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
## 🔒 Security & Integrity
|
|
384
|
-
|
|
385
|
-
The SDK includes built-in integrity verification to prevent cheating and ensure fair play:
|
|
386
|
-
|
|
387
|
-
### Automatic Features
|
|
388
|
-
|
|
389
|
-
- **Session Integrity**: Each game session includes a unique session token and salt
|
|
390
|
-
- **Game State Hashing**: Game states are automatically hashed using SHA-256 with session salt
|
|
391
|
-
- **Integrity Headers**: All authenticated requests include cryptographic integrity headers
|
|
392
|
-
- **Retry Logic**: Failed requests due to integrity issues are automatically retried with fresh headers
|
|
393
|
-
|
|
394
|
-
### How It Works
|
|
395
|
-
|
|
396
|
-
1. **Session Initialization**: SDK receives session token and salt from server
|
|
397
|
-
2. **State Hashing**: Game states are canonicalized and hashed with session salt
|
|
398
|
-
3. **Request Signing**: Integrity headers are built with timestamp, nonce, and session token
|
|
399
|
-
4. **Server Validation**: Server verifies hashes and headers to detect tampering
|
|
400
|
-
|
|
401
|
-
### Manual Verification (Advanced)
|
|
402
|
-
|
|
403
|
-
```typescript
|
|
404
|
-
// Manually validate game session
|
|
405
|
-
const validation = await sdk.validateGameSession({
|
|
406
|
-
clientChecksum: "calculated-checksum",
|
|
407
|
-
stateSnapshot: currentGameState,
|
|
408
|
-
timingData: { avgResponseTime: 250 },
|
|
409
|
-
behaviorMetrics: { clicksPerSecond: 5.2 },
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
if (!validation.isValid) {
|
|
413
|
-
console.log("Validation issues:", validation.validationIssues);
|
|
414
|
-
}
|
|
415
|
-
```
|
|
416
|
-
|
|
417
|
-
## 🧪 Testing & Development
|
|
418
|
-
|
|
419
|
-
### Testing
|
|
420
|
-
|
|
421
|
-
```typescript
|
|
422
|
-
// Create a test game session for development
|
|
423
|
-
const testSession = await sdk.createTestGameSession({
|
|
424
|
-
testConfig: {
|
|
425
|
-
mockCampaign: true,
|
|
426
|
-
mockPlayers: 10,
|
|
427
|
-
testScenario: "leaderboard",
|
|
428
|
-
},
|
|
429
|
-
});
|
|
430
|
-
|
|
431
|
-
console.log("Test session created:", testSession.testSessionId);
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
### Debug Mode
|
|
435
|
-
|
|
436
|
-
Enable debug mode to see detailed logging:
|
|
437
|
-
|
|
438
|
-
```typescript
|
|
439
|
-
const sdk = createPlayeSDK({
|
|
440
|
-
apiBaseUrl: "https://api.playe.com",
|
|
441
|
-
enableDebugMode: true, // This will log all API calls, integrity operations, and responses
|
|
442
|
-
});
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
### URL Requirements
|
|
446
|
-
|
|
447
|
-
For `initializeGameSession()` to work, page URL must include a `sessionId` query parameter:
|
|
448
|
-
|
|
449
|
-
```
|
|
450
|
-
https://yourgame.com/play?sessionId=abc123-def456-ghi789
|
|
451
|
-
```
|
|
452
|
-
|
|
453
|
-
The SDK will automatically extract this sessionId and initialize session.
|
|
454
|
-
|
|
455
|
-
## 📚 Examples
|
|
456
|
-
|
|
457
|
-
### Browser Example
|
|
458
|
-
|
|
459
|
-
The `examples/browser-example.html` provides a complete integration example with:
|
|
460
|
-
|
|
461
|
-
- SDK initialization and configuration
|
|
462
|
-
- Session management (initialize, start, update, complete)
|
|
463
|
-
- Progress tracking with integrity verification
|
|
464
|
-
- Leaderboard fetching and display
|
|
465
|
-
- Error handling and recovery
|
|
466
|
-
- Debug mode logging
|
|
467
|
-
- Device fingerprinting
|
|
468
|
-
|
|
469
|
-
### Flappy Bird Example
|
|
470
|
-
|
|
471
|
-
The `examples/flappy-bird.html` provides a complete game integration example with:
|
|
472
|
-
|
|
473
|
-
- Player eligibility validation (attempts remaining, campaign status)
|
|
474
|
-
- Game session initialization with SDK
|
|
475
|
-
- Real-time progress tracking
|
|
476
|
-
- Score submission with completion data
|
|
477
|
-
- Graceful fallback to demo mode if SDK unavailable
|
|
478
|
-
- Responsive game canvas with mobile support
|
|
479
|
-
|
|
480
|
-
## 🔄 Migration Guide
|
|
481
|
-
|
|
482
|
-
### Migrating from v0.x to v1.x
|
|
483
|
-
|
|
484
|
-
#### Breaking Changes
|
|
485
|
-
|
|
486
|
-
1. **SDK Initialization**
|
|
487
|
-
```typescript
|
|
488
|
-
// v0.x
|
|
489
|
-
import { PlayeDeveloperSDK } from 'playe-developer-sdk';
|
|
490
|
-
const sdk = new PlayeDeveloperSDK(config);
|
|
491
|
-
|
|
492
|
-
// v1.x
|
|
493
|
-
import { createPlayeSDK } from 'playe-developer-sdk';
|
|
494
|
-
const sdk = createPlayeSDK(config);
|
|
495
|
-
```
|
|
496
|
-
|
|
497
|
-
2. **Method Signatures**
|
|
498
|
-
```typescript
|
|
499
|
-
// v0.x - Callback-based
|
|
500
|
-
sdk.initializeGameSession((error, session) => {
|
|
501
|
-
if (error) handleError(error);
|
|
502
|
-
else handleSuccess(session);
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
// v1.x - Promise-based
|
|
506
|
-
try {
|
|
507
|
-
const session = await sdk.initializeGameSession();
|
|
508
|
-
handleSuccess(session);
|
|
509
|
-
} catch (error) {
|
|
510
|
-
handleError(error);
|
|
511
|
-
}
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
## 🚨 Error Handling
|
|
515
|
-
|
|
516
|
-
### Error Types
|
|
517
|
-
|
|
518
|
-
```typescript
|
|
519
|
-
// Base SDK error
|
|
520
|
-
PlayeSDKError {
|
|
521
|
-
name: string;
|
|
522
|
-
message: string;
|
|
523
|
-
originalError?: Error;
|
|
524
|
-
context?: string;
|
|
525
|
-
timestamp: string;
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// Specific error types
|
|
529
|
-
ValidationError extends PlayeSDKError; // Validation failures
|
|
530
|
-
NetworkError extends PlayeSDKError; // Network issues
|
|
531
|
-
AuthenticationError extends PlayeSDKError; // Authentication failures
|
|
532
|
-
GameSessionError extends PlayeSDKError; // Session-specific errors
|
|
533
|
-
```
|
|
534
|
-
|
|
535
|
-
### Error Recovery
|
|
536
|
-
|
|
537
|
-
```typescript
|
|
538
|
-
// Enable debug mode for detailed error information
|
|
539
|
-
const sdk = createPlayeSDK({
|
|
540
|
-
apiBaseUrl: 'https://api.playe.com',
|
|
541
|
-
enableDebugMode: true,
|
|
542
|
-
retryAttempts: 3, // Configure retry behavior
|
|
543
|
-
timeoutMs: 30000, // Configure timeout
|
|
544
|
-
});
|
|
545
|
-
```
|
|
546
|
-
|
|
547
|
-
## 🔧 Troubleshooting
|
|
548
|
-
|
|
549
|
-
### Common Issues
|
|
550
|
-
|
|
551
|
-
#### "No sessionId found in URL" Error
|
|
552
|
-
|
|
553
|
-
**Problem:** Getting an error when calling `initializeGameSession()`.
|
|
554
|
-
|
|
555
|
-
**Solution:** Ensure your game URL includes a `sessionId` query parameter:
|
|
556
|
-
```
|
|
557
|
-
https://yourgame.com/play?sessionId=abc123-def456-ghi789
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
#### Network/Timeout Errors
|
|
561
|
-
|
|
562
|
-
**Problem:** API calls failing with network errors.
|
|
563
|
-
|
|
564
|
-
**Solutions:**
|
|
565
|
-
1. Check API endpoint configuration
|
|
566
|
-
2. Verify API key is correct
|
|
567
|
-
3. Check network connectivity
|
|
568
|
-
4. Increase timeout for slow networks
|
|
569
|
-
|
|
570
|
-
#### Integrity Validation Failures
|
|
571
|
-
|
|
572
|
-
**Problem:** Game completion failing with integrity validation errors.
|
|
573
|
-
|
|
574
|
-
**Solutions:**
|
|
575
|
-
1. Ensure game state is valid and serializable
|
|
576
|
-
2. Check timing data is reasonable
|
|
577
|
-
3. Use debug mode to inspect requests
|
|
578
|
-
|
|
579
|
-
## 🤝 Contributing
|
|
580
|
-
|
|
581
|
-
We welcome contributions to the Playe Developer SDK!
|
|
582
|
-
|
|
583
|
-
### Development Setup
|
|
584
|
-
|
|
585
|
-
```bash
|
|
586
|
-
# Clone repository
|
|
587
|
-
git clone https://github.com/playe/playe.git
|
|
588
|
-
cd playe
|
|
589
|
-
|
|
590
|
-
# Install dependencies
|
|
591
|
-
pnpm install
|
|
592
|
-
|
|
593
|
-
# Build SDK
|
|
594
|
-
pnpm --filter playe-developer-sdk build
|
|
595
|
-
|
|
596
|
-
# Run tests
|
|
597
|
-
pnpm --filter playe-developer-sdk test
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
### Code Guidelines
|
|
601
|
-
|
|
602
|
-
- Follow TypeScript best practices
|
|
603
|
-
- Add tests for new functionality
|
|
604
|
-
- Update documentation as needed
|
|
605
|
-
- Ensure all tests pass
|
|
606
|
-
|
|
607
|
-
## 📄 License
|
|
608
|
-
|
|
609
|
-
MIT License - see LICENSE file for details.
|
|
610
|
-
|
|
611
|
-
Copyright (c) 2024 Playe
|
|
612
|
-
|
|
613
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
614
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
615
|
-
in the Software without restriction, including without limitation the rights
|
|
616
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
617
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
618
|
-
furnished to do so, subject to the following conditions:
|
|
619
|
-
|
|
620
|
-
The above copyright notice and this permission notice shall be included in all
|
|
621
|
-
copies or substantial portions of the Software.
|
|
622
|
-
|
|
623
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
624
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
625
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
626
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
627
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
628
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
# 🎮 Playe Developer SDK
|
|
2
|
+
|
|
3
|
+
[](https://npmjs.com/package/playe-developer-sdk)
|
|
4
|
+
[](https://github.com/playe/playe/blob/main/LICENSE)
|
|
5
|
+
[](https://bundlephobia.com/package/playe-developer-sdk)
|
|
6
|
+
[](https://www.typescriptlang.org/)
|
|
7
|
+
|
|
8
|
+
A browser-based SDK for integrating games with the Playe platform. Provides game session management, campaign integration, leaderboards, and integrity verification.
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- 🎮 **Game Session Management** - Initialize, start, update, and complete game sessions
|
|
13
|
+
- 🛡️ **Integrity Verification** - Built-in session integrity with cryptographic hashing
|
|
14
|
+
- 🏆 **Campaign Integration** - Access campaign information, leaderboards, and player attempts
|
|
15
|
+
- 🔄 **Anti-Cheat Protection** - Advanced validation, heartbeat mechanisms, and session monitoring
|
|
16
|
+
- 📱 **Device Detection** - Automatic device information and fingerprinting
|
|
17
|
+
- 🔧 **Developer Tools** - Test game sessions and debugging utilities
|
|
18
|
+
- 📊 **Real-time Updates** - Progress tracking and live leaderboards
|
|
19
|
+
- ⚡ **Browser Optimized** - Lightweight and fast for web games with automatic retry logic
|
|
20
|
+
|
|
21
|
+
## 🚀 Installation
|
|
22
|
+
|
|
23
|
+
### NPM
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install playe-developer-sdk
|
|
27
|
+
# or
|
|
28
|
+
yarn add playe-developer-sdk
|
|
29
|
+
# or
|
|
30
|
+
pnpm add playe-developer-sdk
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### CDN
|
|
34
|
+
|
|
35
|
+
```html
|
|
36
|
+
<script src="https://unpkg.com/playe-developer-sdk@latest/dist/playe-sdk.umd.min.js"></script>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Workspace
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@workspace/playe-developer-sdk": "workspace:*"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## 📖 Quick Start
|
|
50
|
+
|
|
51
|
+
### NPM Usage
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { createPlayeSDK } from "playe-developer-sdk";
|
|
55
|
+
|
|
56
|
+
// Initialize SDK
|
|
57
|
+
const sdk = createPlayeSDK({
|
|
58
|
+
apiBaseUrl: "https://api.playe.com",
|
|
59
|
+
apiKey: "your-api-key",
|
|
60
|
+
enableDebugMode: true,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Initialize game session (gets sessionId from URL)
|
|
64
|
+
const session = await sdk.initializeGameSession();
|
|
65
|
+
|
|
66
|
+
// Start game
|
|
67
|
+
await sdk.startGameSession({
|
|
68
|
+
difficulty: "normal",
|
|
69
|
+
mode: "classic",
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Update progress
|
|
73
|
+
await sdk.updateGameProgress({
|
|
74
|
+
currentScore: 1500,
|
|
75
|
+
elapsedTime: 120,
|
|
76
|
+
gameState: {
|
|
77
|
+
level: 3,
|
|
78
|
+
lives: 2,
|
|
79
|
+
powerUps: ["speed", "shield"],
|
|
80
|
+
},
|
|
81
|
+
achievements: [
|
|
82
|
+
{
|
|
83
|
+
id: "level-3",
|
|
84
|
+
name: "Level 3 Complete",
|
|
85
|
+
unlockedAt: new Date().toISOString(),
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Complete game
|
|
91
|
+
const result = await sdk.completeGame({
|
|
92
|
+
finalScore: 2500,
|
|
93
|
+
validationChecksum: "client-checksum",
|
|
94
|
+
gameMetrics: {
|
|
95
|
+
totalPlayTime: 300,
|
|
96
|
+
actionsPerformed: 150,
|
|
97
|
+
powerUpsUsed: 5,
|
|
98
|
+
levelsCompleted: 5,
|
|
99
|
+
},
|
|
100
|
+
finalGameState: {
|
|
101
|
+
level: 5,
|
|
102
|
+
finalLives: 1,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### CDN Usage
|
|
108
|
+
|
|
109
|
+
```html
|
|
110
|
+
<script src="https://unpkg.com/playe-developer-sdk@latest/dist/playe-sdk.umd.min.js"></script>
|
|
111
|
+
<script>
|
|
112
|
+
const sdk = PlayeSDK.createPlayeSDK({
|
|
113
|
+
apiBaseUrl: 'https://api.playe.com',
|
|
114
|
+
apiKey: 'your-api-key',
|
|
115
|
+
enableDebugMode: true,
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const session = await sdk.initializeGameSession();
|
|
119
|
+
console.log('Session initialized:', session.sessionId);
|
|
120
|
+
</script>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## 📋 Table of Contents
|
|
124
|
+
|
|
125
|
+
- [🎮 Playe Developer SDK](#-playe-developer-sdk)
|
|
126
|
+
- [✨ Features](#-features)
|
|
127
|
+
- [🚀 Installation](#-installation)
|
|
128
|
+
- [📖 Quick Start](#-quick-start)
|
|
129
|
+
- [📋 Table of Contents](#-table-of-contents)
|
|
130
|
+
- [🔧 API Reference](#-api-reference)
|
|
131
|
+
- [🔒 Security & Integrity](#-security--integrity)
|
|
132
|
+
- [🧪 Testing & Development](#-testing--development)
|
|
133
|
+
- [🚨 Error Handling](#-error-handling)
|
|
134
|
+
- [📚 Examples](#-examples)
|
|
135
|
+
- [🔄 Migration Guide](#-migration-guide)
|
|
136
|
+
- [🔧 Troubleshooting](#-troubleshooting)
|
|
137
|
+
- [🤝 Contributing](#-contributing)
|
|
138
|
+
- [📄 License](#-license)
|
|
139
|
+
|
|
140
|
+
## 🔧 API Reference
|
|
141
|
+
|
|
142
|
+
### SDK Configuration
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface SDKConfig {
|
|
146
|
+
apiBaseUrl: string; // Required: API base URL
|
|
147
|
+
apiKey?: string; // Optional: API key for authentication
|
|
148
|
+
enableDebugMode?: boolean; // Optional: Enable debug logging
|
|
149
|
+
retryAttempts?: number; // Optional: Number of retry attempts (default: 3)
|
|
150
|
+
timeoutMs?: number; // Optional: Request timeout in ms (default: 30000)
|
|
151
|
+
enableOfflineMode?: boolean; // Optional: Enable offline mode (default: false)
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Core Methods
|
|
156
|
+
|
|
157
|
+
#### Game Session Management
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
// Initialize game session (gets sessionId from URL query parameter)
|
|
161
|
+
const session = await sdk.initializeGameSession(): Promise<GameSessionResponse>;
|
|
162
|
+
// Returns: { sessionId, campaignId, gameId, status, campaign, player, createdAt, expiresAt }
|
|
163
|
+
|
|
164
|
+
// Start game session
|
|
165
|
+
await sdk.startGameSession(config?: Record<string, any>): Promise<GameSessionResponse>;
|
|
166
|
+
// Returns: { sessionId, status, startTime }
|
|
167
|
+
|
|
168
|
+
// Update game progress with integrity verification
|
|
169
|
+
await sdk.updateGameProgress({
|
|
170
|
+
currentScore: number;
|
|
171
|
+
gameState?: Record<string, any>; // Automatically hashed for integrity
|
|
172
|
+
elapsedTime: number;
|
|
173
|
+
achievements?: Achievement[];
|
|
174
|
+
clientTimestamp?: string;
|
|
175
|
+
}): Promise<ProgressUpdateResponse>;
|
|
176
|
+
// Returns: { isValid, validationMessage, currentRank, isLeading, campaignStatus, warnings }
|
|
177
|
+
|
|
178
|
+
// Complete game with integrity verification
|
|
179
|
+
await sdk.completeGame({
|
|
180
|
+
finalScore: number;
|
|
181
|
+
validationChecksum: string;
|
|
182
|
+
gameMetrics?: GameMetrics;
|
|
183
|
+
finalGameState?: Record<string, any>; // Automatically hashed for integrity
|
|
184
|
+
}): Promise<GameCompletionResponse>;
|
|
185
|
+
// Returns: { isWinner, prizeAmount, prizeType, finalRank, totalParticipants, winnerDetails, reward, campaignResult }
|
|
186
|
+
|
|
187
|
+
// Abandon game
|
|
188
|
+
await sdk.abandonGame(reason?: string, lastKnownScore?: number): Promise<AbandonGameResponse>;
|
|
189
|
+
// Returns: { success, message, gamepassRefunded, abandonedAt }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
#### Campaign Information
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
// Get campaign status
|
|
196
|
+
await sdk.getCampaignStatus(campaignId: string, playerId?: string): Promise<CampaignStatusResponse>;
|
|
197
|
+
// Returns: { campaignId, status, campaignStyle, remainingGamepasses, totalParticipants,
|
|
198
|
+
// totalPrizePool, endDate, playerStatus, topPlayers, prizeBreakdown }
|
|
199
|
+
|
|
200
|
+
// Get leaderboard
|
|
201
|
+
await sdk.getLeaderboard(campaignId: string, limit?: number, playerId?: string): Promise<LeaderboardResponse>;
|
|
202
|
+
// Returns: { campaignId, entries, playerRank, lastUpdated, isLive }
|
|
203
|
+
// entries: [{ rank, playerId, displayName, score, achievedAt, isCurrentPlayer, prizeAmount }]
|
|
204
|
+
|
|
205
|
+
// Get player attempts
|
|
206
|
+
await sdk.getPlayerAttempts(campaignId: string, playerId: string): Promise<PlayerAttemptsResponse>;
|
|
207
|
+
// Returns: { playerId, campaignId, totalAttempts, remainingAttempts, attempts, stats }
|
|
208
|
+
// attempts: [{ sessionId, score, playedAt, duration, status, wasWinner }]
|
|
209
|
+
// stats: { bestScore, averageScore, totalPlayTime, firstAttempt, lastAttempt }
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### Validation & Anti-Cheat
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
// Validate game session with integrity verification
|
|
216
|
+
await sdk.validateGameSession({
|
|
217
|
+
clientChecksum: string;
|
|
218
|
+
stateSnapshot?: Record<string, unknown>; // Automatically hashed
|
|
219
|
+
timingData?: unknown;
|
|
220
|
+
behaviorMetrics?: unknown;
|
|
221
|
+
}): Promise<ValidationResponse>;
|
|
222
|
+
// Returns: { isValid, confidenceScore, validationIssues, warnings, requiresRevalidation, validatedAt }
|
|
223
|
+
|
|
224
|
+
// Send heartbeat (usually handled automatically)
|
|
225
|
+
await sdk.sendHeartbeat(currentScore?: number): Promise<HeartbeatResponse>;
|
|
226
|
+
// Returns: { acknowledged, serverTimestamp, nextHeartbeatIn, campaignStillActive, message }
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Developer Tools
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
// Create test game session for development
|
|
233
|
+
await sdk.createTestGameSession(testConfig?: Record<string, any>): Promise<TestGameResponse>;
|
|
234
|
+
// Returns: { testSessionId, testScenario, mockCampaign, testData, expiresAt }
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
### Utility Methods
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// Test SDK functionality
|
|
241
|
+
sdk.test('Custom test message'): void;
|
|
242
|
+
|
|
243
|
+
// Check if running in demo mode
|
|
244
|
+
const isDemoMode = sdk.isDemo(): boolean;
|
|
245
|
+
|
|
246
|
+
// Game lifecycle events
|
|
247
|
+
sdk.gameLoadingStart(): void; // Emit loading start event
|
|
248
|
+
sdk.gameLoadingFinished(): void; // Emit loading finished event
|
|
249
|
+
sdk.gamePlayStop(): void; // Stop heartbeat and emit stop event
|
|
250
|
+
|
|
251
|
+
// Clean up resources
|
|
252
|
+
sdk.destroy(): void; // Clean up session artifacts and stop heartbeat
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Response Types
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
// Game Session Response
|
|
259
|
+
interface GameSessionResponse {
|
|
260
|
+
sessionId: string;
|
|
261
|
+
campaignId: string;
|
|
262
|
+
gameId: string;
|
|
263
|
+
status: string;
|
|
264
|
+
campaign: CampaignInfo;
|
|
265
|
+
player: PlayerInfo;
|
|
266
|
+
createdAt: string;
|
|
267
|
+
expiresAt?: string;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Progress Update Response
|
|
271
|
+
interface ProgressUpdateResponse {
|
|
272
|
+
isValid: boolean;
|
|
273
|
+
validationMessage?: string;
|
|
274
|
+
currentRank: number;
|
|
275
|
+
isLeading: boolean;
|
|
276
|
+
campaignStatus?: CampaignStatusInfo;
|
|
277
|
+
warnings?: string[];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Game Completion Response
|
|
281
|
+
interface GameCompletionResponse {
|
|
282
|
+
isWinner: boolean;
|
|
283
|
+
prizeAmount?: number;
|
|
284
|
+
prizeType?: string;
|
|
285
|
+
finalRank: number;
|
|
286
|
+
totalParticipants: number;
|
|
287
|
+
winnerDetails?: WinnerInfo;
|
|
288
|
+
reward?: RewardInfo;
|
|
289
|
+
campaignResult?: CampaignResultInfo;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Campaign Status Response
|
|
293
|
+
interface CampaignStatusResponse {
|
|
294
|
+
campaignId: string;
|
|
295
|
+
status: string;
|
|
296
|
+
campaignStyle: string;
|
|
297
|
+
remainingGamepasses: number;
|
|
298
|
+
totalParticipants: number;
|
|
299
|
+
totalPrizePool: number;
|
|
300
|
+
endDate?: string;
|
|
301
|
+
playerStatus?: PlayerStatusInfo;
|
|
302
|
+
topPlayers?: TopPlayer[];
|
|
303
|
+
prizeBreakdown?: PrizeBreakdown;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Leaderboard Response
|
|
307
|
+
interface LeaderboardResponse {
|
|
308
|
+
campaignId: string;
|
|
309
|
+
entries: LeaderboardEntry[];
|
|
310
|
+
playerRank?: PlayerRankInfo;
|
|
311
|
+
lastUpdated: string;
|
|
312
|
+
isLive: boolean;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Validation Response
|
|
316
|
+
interface ValidationResponse {
|
|
317
|
+
isValid: boolean;
|
|
318
|
+
confidenceScore: number;
|
|
319
|
+
validationIssues: string[];
|
|
320
|
+
warnings: string[];
|
|
321
|
+
requiresRevalidation: boolean;
|
|
322
|
+
validatedAt: string;
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Integrity Verification Utilities
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
import { canonicalStringify, sha256Hex } from "playe-developer-sdk";
|
|
330
|
+
|
|
331
|
+
// Canonical JSON stringification (deterministic)
|
|
332
|
+
const canonical = canonicalStringify({ b: 1, a: 2 }); // '{"a":2,"b":1}'
|
|
333
|
+
|
|
334
|
+
// SHA-256 hashing
|
|
335
|
+
const hash = await sha256Hex("data to hash");
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Device Utilities
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { DeviceUtils } from "@workspace/playe-developer-sdk";
|
|
342
|
+
|
|
343
|
+
// Get device information
|
|
344
|
+
const deviceInfo = DeviceUtils.getDeviceInfo();
|
|
345
|
+
|
|
346
|
+
// Get geolocation
|
|
347
|
+
const location = await DeviceUtils.getGeolocationData();
|
|
348
|
+
|
|
349
|
+
// Generate fingerprint
|
|
350
|
+
const fingerprint = DeviceUtils.generateFingerprint();
|
|
351
|
+
|
|
352
|
+
// Check browser support
|
|
353
|
+
const isSupported = DeviceUtils.isBrowser();
|
|
354
|
+
const features = DeviceUtils.getFeatureSupport();
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Error Handling
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
import {
|
|
361
|
+
PlayeSDKError,
|
|
362
|
+
ValidationError,
|
|
363
|
+
NetworkError,
|
|
364
|
+
AuthenticationError,
|
|
365
|
+
GameSessionError,
|
|
366
|
+
} from "@workspace/playe-developer-sdk";
|
|
367
|
+
|
|
368
|
+
try {
|
|
369
|
+
await sdk.initializeGameSession(params);
|
|
370
|
+
} catch (error) {
|
|
371
|
+
if (error instanceof ValidationError) {
|
|
372
|
+
console.error("Validation failed:", error.message);
|
|
373
|
+
} else if (error instanceof NetworkError) {
|
|
374
|
+
console.error("Network error:", error.message);
|
|
375
|
+
} else if (error instanceof AuthenticationError) {
|
|
376
|
+
console.error("Authentication failed:", error.message);
|
|
377
|
+
} else if (error instanceof PlayeSDKError) {
|
|
378
|
+
console.error("SDK error:", error.toJSON());
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## 🔒 Security & Integrity
|
|
384
|
+
|
|
385
|
+
The SDK includes built-in integrity verification to prevent cheating and ensure fair play:
|
|
386
|
+
|
|
387
|
+
### Automatic Features
|
|
388
|
+
|
|
389
|
+
- **Session Integrity**: Each game session includes a unique session token and salt
|
|
390
|
+
- **Game State Hashing**: Game states are automatically hashed using SHA-256 with session salt
|
|
391
|
+
- **Integrity Headers**: All authenticated requests include cryptographic integrity headers
|
|
392
|
+
- **Retry Logic**: Failed requests due to integrity issues are automatically retried with fresh headers
|
|
393
|
+
|
|
394
|
+
### How It Works
|
|
395
|
+
|
|
396
|
+
1. **Session Initialization**: SDK receives session token and salt from server
|
|
397
|
+
2. **State Hashing**: Game states are canonicalized and hashed with session salt
|
|
398
|
+
3. **Request Signing**: Integrity headers are built with timestamp, nonce, and session token
|
|
399
|
+
4. **Server Validation**: Server verifies hashes and headers to detect tampering
|
|
400
|
+
|
|
401
|
+
### Manual Verification (Advanced)
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
// Manually validate game session
|
|
405
|
+
const validation = await sdk.validateGameSession({
|
|
406
|
+
clientChecksum: "calculated-checksum",
|
|
407
|
+
stateSnapshot: currentGameState,
|
|
408
|
+
timingData: { avgResponseTime: 250 },
|
|
409
|
+
behaviorMetrics: { clicksPerSecond: 5.2 },
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
if (!validation.isValid) {
|
|
413
|
+
console.log("Validation issues:", validation.validationIssues);
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
## 🧪 Testing & Development
|
|
418
|
+
|
|
419
|
+
### Testing
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// Create a test game session for development
|
|
423
|
+
const testSession = await sdk.createTestGameSession({
|
|
424
|
+
testConfig: {
|
|
425
|
+
mockCampaign: true,
|
|
426
|
+
mockPlayers: 10,
|
|
427
|
+
testScenario: "leaderboard",
|
|
428
|
+
},
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
console.log("Test session created:", testSession.testSessionId);
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Debug Mode
|
|
435
|
+
|
|
436
|
+
Enable debug mode to see detailed logging:
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
const sdk = createPlayeSDK({
|
|
440
|
+
apiBaseUrl: "https://api.playe.com",
|
|
441
|
+
enableDebugMode: true, // This will log all API calls, integrity operations, and responses
|
|
442
|
+
});
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### URL Requirements
|
|
446
|
+
|
|
447
|
+
For `initializeGameSession()` to work, page URL must include a `sessionId` query parameter:
|
|
448
|
+
|
|
449
|
+
```
|
|
450
|
+
https://yourgame.com/play?sessionId=abc123-def456-ghi789
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
The SDK will automatically extract this sessionId and initialize session.
|
|
454
|
+
|
|
455
|
+
## 📚 Examples
|
|
456
|
+
|
|
457
|
+
### Browser Example
|
|
458
|
+
|
|
459
|
+
The `examples/browser-example.html` provides a complete integration example with:
|
|
460
|
+
|
|
461
|
+
- SDK initialization and configuration
|
|
462
|
+
- Session management (initialize, start, update, complete)
|
|
463
|
+
- Progress tracking with integrity verification
|
|
464
|
+
- Leaderboard fetching and display
|
|
465
|
+
- Error handling and recovery
|
|
466
|
+
- Debug mode logging
|
|
467
|
+
- Device fingerprinting
|
|
468
|
+
|
|
469
|
+
### Flappy Bird Example
|
|
470
|
+
|
|
471
|
+
The `examples/flappy-bird.html` provides a complete game integration example with:
|
|
472
|
+
|
|
473
|
+
- Player eligibility validation (attempts remaining, campaign status)
|
|
474
|
+
- Game session initialization with SDK
|
|
475
|
+
- Real-time progress tracking
|
|
476
|
+
- Score submission with completion data
|
|
477
|
+
- Graceful fallback to demo mode if SDK unavailable
|
|
478
|
+
- Responsive game canvas with mobile support
|
|
479
|
+
|
|
480
|
+
## 🔄 Migration Guide
|
|
481
|
+
|
|
482
|
+
### Migrating from v0.x to v1.x
|
|
483
|
+
|
|
484
|
+
#### Breaking Changes
|
|
485
|
+
|
|
486
|
+
1. **SDK Initialization**
|
|
487
|
+
```typescript
|
|
488
|
+
// v0.x
|
|
489
|
+
import { PlayeDeveloperSDK } from 'playe-developer-sdk';
|
|
490
|
+
const sdk = new PlayeDeveloperSDK(config);
|
|
491
|
+
|
|
492
|
+
// v1.x
|
|
493
|
+
import { createPlayeSDK } from 'playe-developer-sdk';
|
|
494
|
+
const sdk = createPlayeSDK(config);
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
2. **Method Signatures**
|
|
498
|
+
```typescript
|
|
499
|
+
// v0.x - Callback-based
|
|
500
|
+
sdk.initializeGameSession((error, session) => {
|
|
501
|
+
if (error) handleError(error);
|
|
502
|
+
else handleSuccess(session);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
// v1.x - Promise-based
|
|
506
|
+
try {
|
|
507
|
+
const session = await sdk.initializeGameSession();
|
|
508
|
+
handleSuccess(session);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
handleError(error);
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## 🚨 Error Handling
|
|
515
|
+
|
|
516
|
+
### Error Types
|
|
517
|
+
|
|
518
|
+
```typescript
|
|
519
|
+
// Base SDK error
|
|
520
|
+
PlayeSDKError {
|
|
521
|
+
name: string;
|
|
522
|
+
message: string;
|
|
523
|
+
originalError?: Error;
|
|
524
|
+
context?: string;
|
|
525
|
+
timestamp: string;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Specific error types
|
|
529
|
+
ValidationError extends PlayeSDKError; // Validation failures
|
|
530
|
+
NetworkError extends PlayeSDKError; // Network issues
|
|
531
|
+
AuthenticationError extends PlayeSDKError; // Authentication failures
|
|
532
|
+
GameSessionError extends PlayeSDKError; // Session-specific errors
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### Error Recovery
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
// Enable debug mode for detailed error information
|
|
539
|
+
const sdk = createPlayeSDK({
|
|
540
|
+
apiBaseUrl: 'https://api.playe.com',
|
|
541
|
+
enableDebugMode: true,
|
|
542
|
+
retryAttempts: 3, // Configure retry behavior
|
|
543
|
+
timeoutMs: 30000, // Configure timeout
|
|
544
|
+
});
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## 🔧 Troubleshooting
|
|
548
|
+
|
|
549
|
+
### Common Issues
|
|
550
|
+
|
|
551
|
+
#### "No sessionId found in URL" Error
|
|
552
|
+
|
|
553
|
+
**Problem:** Getting an error when calling `initializeGameSession()`.
|
|
554
|
+
|
|
555
|
+
**Solution:** Ensure your game URL includes a `sessionId` query parameter:
|
|
556
|
+
```
|
|
557
|
+
https://yourgame.com/play?sessionId=abc123-def456-ghi789
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
#### Network/Timeout Errors
|
|
561
|
+
|
|
562
|
+
**Problem:** API calls failing with network errors.
|
|
563
|
+
|
|
564
|
+
**Solutions:**
|
|
565
|
+
1. Check API endpoint configuration
|
|
566
|
+
2. Verify API key is correct
|
|
567
|
+
3. Check network connectivity
|
|
568
|
+
4. Increase timeout for slow networks
|
|
569
|
+
|
|
570
|
+
#### Integrity Validation Failures
|
|
571
|
+
|
|
572
|
+
**Problem:** Game completion failing with integrity validation errors.
|
|
573
|
+
|
|
574
|
+
**Solutions:**
|
|
575
|
+
1. Ensure game state is valid and serializable
|
|
576
|
+
2. Check timing data is reasonable
|
|
577
|
+
3. Use debug mode to inspect requests
|
|
578
|
+
|
|
579
|
+
## 🤝 Contributing
|
|
580
|
+
|
|
581
|
+
We welcome contributions to the Playe Developer SDK!
|
|
582
|
+
|
|
583
|
+
### Development Setup
|
|
584
|
+
|
|
585
|
+
```bash
|
|
586
|
+
# Clone repository
|
|
587
|
+
git clone https://github.com/playe/playe.git
|
|
588
|
+
cd playe
|
|
589
|
+
|
|
590
|
+
# Install dependencies
|
|
591
|
+
pnpm install
|
|
592
|
+
|
|
593
|
+
# Build SDK
|
|
594
|
+
pnpm --filter playe-developer-sdk build
|
|
595
|
+
|
|
596
|
+
# Run tests
|
|
597
|
+
pnpm --filter playe-developer-sdk test
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
### Code Guidelines
|
|
601
|
+
|
|
602
|
+
- Follow TypeScript best practices
|
|
603
|
+
- Add tests for new functionality
|
|
604
|
+
- Update documentation as needed
|
|
605
|
+
- Ensure all tests pass
|
|
606
|
+
|
|
607
|
+
## 📄 License
|
|
608
|
+
|
|
609
|
+
MIT License - see LICENSE file for details.
|
|
610
|
+
|
|
611
|
+
Copyright (c) 2024 Playe
|
|
612
|
+
|
|
613
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
614
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
615
|
+
in the Software without restriction, including without limitation the rights
|
|
616
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
617
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
618
|
+
furnished to do so, subject to the following conditions:
|
|
619
|
+
|
|
620
|
+
The above copyright notice and this permission notice shall be included in all
|
|
621
|
+
copies or substantial portions of the Software.
|
|
622
|
+
|
|
623
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
624
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
625
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
626
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
627
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
628
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
629
629
|
SOFTWARE.
|