@playcademy/sdk 0.0.1-beta.8 → 0.0.1
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 +511 -124
- package/dist/core/auth/flows/popup.d.ts +14 -0
- package/dist/core/auth/flows/redirect.d.ts +15 -0
- package/dist/core/auth/flows/unified.d.ts +11 -0
- package/dist/core/auth/login.d.ts +20 -0
- package/dist/core/auth/oauth.d.ts +115 -0
- package/dist/core/auth/utils.d.ts +23 -0
- package/dist/core/cache/cooldown-cache.d.ts +31 -0
- package/dist/core/cache/index.d.ts +14 -0
- package/dist/core/cache/permanent-cache.d.ts +39 -0
- package/dist/core/cache/singleton-cache.d.ts +29 -0
- package/dist/core/cache/ttl-cache.d.ts +54 -0
- package/dist/core/cache/types.d.ts +23 -0
- package/dist/core/client.d.ts +444 -68
- package/dist/core/namespaces/achievements.d.ts +84 -0
- package/dist/core/namespaces/admin.d.ts +385 -0
- package/dist/core/namespaces/auth.d.ts +54 -0
- package/dist/core/namespaces/character.d.ts +205 -0
- package/dist/core/namespaces/credits.d.ts +51 -0
- package/dist/core/namespaces/dev.d.ts +323 -0
- package/dist/core/namespaces/games.d.ts +173 -0
- package/dist/core/namespaces/identity.d.ts +91 -0
- package/dist/core/namespaces/index.d.ts +19 -0
- package/dist/core/namespaces/leaderboard.d.ts +48 -0
- package/dist/core/namespaces/levels.d.ts +90 -0
- package/dist/core/namespaces/maps.d.ts +93 -0
- package/dist/core/namespaces/realtime.client.d.ts +129 -0
- package/dist/core/namespaces/realtime.d.ts +90 -0
- package/dist/core/namespaces/runtime.d.ts +222 -0
- package/dist/core/namespaces/scores.d.ts +55 -0
- package/dist/core/namespaces/shop.d.ts +25 -0
- package/dist/core/namespaces/sprites.d.ts +35 -0
- package/dist/core/namespaces/telemetry.d.ts +28 -0
- package/dist/core/namespaces/timeback.d.ts +111 -0
- package/dist/core/namespaces/users.d.ts +172 -0
- package/dist/core/request.d.ts +1 -1
- package/dist/core/static/identity.d.ts +37 -0
- package/dist/core/static/index.d.ts +3 -0
- package/dist/core/static/init.d.ts +21 -0
- package/dist/core/static/login.d.ts +34 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +2846 -0
- package/dist/messaging.d.ts +544 -0
- package/dist/types.d.ts +168 -8
- package/dist/types.js +748 -0
- package/package.json +18 -11
- package/dist/bus.d.ts +0 -37
- package/dist/runtime.d.ts +0 -7
- package/dist/runtime.js +0 -363
package/README.md
CHANGED
|
@@ -1,169 +1,556 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @playcademy/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Official TypeScript SDK for the Playcademy platform**
|
|
4
|
+
|
|
5
|
+
The Playcademy SDK provides a comprehensive, type-safe interface for building games on the Playcademy platform. It handles authentication, game sessions, user data, inventory management, and all platform APIs through a unified client interface.
|
|
4
6
|
|
|
5
7
|
## Overview
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
The SDK serves as the primary interface between your game and the Playcademy platform, providing:
|
|
10
|
+
|
|
11
|
+
- **Automatic Environment Detection**: Seamlessly works in development and production
|
|
12
|
+
- **Type-Safe API Access**: Full TypeScript support with comprehensive type definitions
|
|
13
|
+
- **Session Management**: Automatic game session handling and state persistence
|
|
14
|
+
- **Event System**: Real-time notifications for inventory changes, level ups, and more
|
|
15
|
+
- **Developer Tools**: Built-in support for game development and testing workflows
|
|
16
|
+
|
|
17
|
+
### Key Benefits
|
|
18
|
+
|
|
19
|
+
- **Zero Configuration**: Automatic initialization with environment detection
|
|
20
|
+
- **Production Ready**: Battle-tested API patterns with robust error handling
|
|
21
|
+
- **Real-Time Communication**: Open game-scoped WebSocket channels for multiplayer features.
|
|
22
|
+
- **Event System**: Subscribe to platform events like inventory changes and level ups
|
|
23
|
+
- **Comprehensive Coverage**: Access to all Playcademy platform features
|
|
24
|
+
- **Development Experience**: Integrated with sandbox environment for local development
|
|
25
|
+
|
|
26
|
+
### Use Cases
|
|
8
27
|
|
|
9
|
-
|
|
28
|
+
- **Game Development**: Primary SDK for building games on Playcademy
|
|
29
|
+
- **Web Applications**: Frontend applications interacting with the platform
|
|
30
|
+
- **Developer Tools**: Scripts and utilities for game management
|
|
31
|
+
- **Server Integration**: Backend services integrating with Playcademy APIs
|
|
32
|
+
- **Testing & Automation**: Automated testing of platform integrations
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install the SDK using your preferred package manager:
|
|
10
37
|
|
|
11
38
|
```bash
|
|
39
|
+
# Using Bun (recommended)
|
|
12
40
|
bun add @playcademy/sdk
|
|
13
|
-
|
|
41
|
+
|
|
42
|
+
# Using npm
|
|
14
43
|
npm install @playcademy/sdk
|
|
15
|
-
|
|
44
|
+
|
|
45
|
+
# Using yarn
|
|
16
46
|
yarn add @playcademy/sdk
|
|
17
|
-
|
|
47
|
+
|
|
48
|
+
# Using pnpm
|
|
18
49
|
pnpm add @playcademy/sdk
|
|
19
50
|
```
|
|
20
51
|
|
|
21
|
-
##
|
|
52
|
+
## Quick Start
|
|
22
53
|
|
|
23
|
-
###
|
|
54
|
+
### Automatic Initialization (Recommended)
|
|
24
55
|
|
|
25
|
-
For
|
|
56
|
+
For most game development scenarios, use automatic initialization:
|
|
26
57
|
|
|
27
|
-
```
|
|
28
|
-
import {
|
|
58
|
+
```typescript
|
|
59
|
+
import { PlaycademyClient } from '@playcademy/sdk'
|
|
29
60
|
|
|
30
|
-
|
|
31
|
-
|
|
61
|
+
async function initializeGame() {
|
|
62
|
+
try {
|
|
63
|
+
// Automatic initialization - detects environment and configures appropriately
|
|
64
|
+
const client = await PlaycademyClient.init()
|
|
65
|
+
|
|
66
|
+
// Get current user
|
|
67
|
+
const user = await client.users.me()
|
|
68
|
+
console.log('Welcome,', user.name)
|
|
69
|
+
|
|
70
|
+
// The client is ready for all platform operations
|
|
71
|
+
return client
|
|
72
|
+
} catch (error) {
|
|
73
|
+
console.error('Failed to initialize Playcademy SDK:', error)
|
|
74
|
+
throw error
|
|
75
|
+
}
|
|
76
|
+
}
|
|
32
77
|
```
|
|
33
78
|
|
|
34
|
-
###
|
|
79
|
+
### Environment Detection
|
|
35
80
|
|
|
36
|
-
|
|
81
|
+
The SDK automatically detects and configures for different environments:
|
|
37
82
|
|
|
38
|
-
|
|
39
|
-
|
|
83
|
+
- **Development**: Connects to local sandbox (started by `@playcademy/vite-plugin`)
|
|
84
|
+
- **Production**: Receives configuration from Playcademy platform loader
|
|
85
|
+
- **Testing**: Falls back to mock configuration for automated testing
|
|
40
86
|
|
|
41
|
-
|
|
42
|
-
const baseUrl = 'https://api.playcademy.com' // Or your local /api endpoint
|
|
43
|
-
let loginResponse: LoginResponse
|
|
87
|
+
## Core Features
|
|
44
88
|
|
|
45
|
-
|
|
46
|
-
// 1. Authenticate to get a token
|
|
47
|
-
loginResponse = await PlaycademyClient.login(
|
|
48
|
-
baseUrl,
|
|
49
|
-
'user@example.com',
|
|
50
|
-
'password',
|
|
51
|
-
)
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.error('Login failed:', error)
|
|
54
|
-
return
|
|
55
|
-
}
|
|
89
|
+
### Game Session Management
|
|
56
90
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
baseUrl: baseUrl,
|
|
61
|
-
token: loginResponse.token,
|
|
62
|
-
// gameId: 'your-game-id' // Optional: if provided, client attempts to auto-start/end session
|
|
63
|
-
})
|
|
91
|
+
```typescript
|
|
92
|
+
// Automatic session management when gameId is available
|
|
93
|
+
const client = await PlaycademyClient.init()
|
|
64
94
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
95
|
+
// Save transient game state (position, health, temporary data)
|
|
96
|
+
await client.games.saveState({
|
|
97
|
+
currentLevel: 'forest_glade',
|
|
98
|
+
playerPosition: { x: 100, y: 200 },
|
|
99
|
+
health: 85,
|
|
100
|
+
activePowerUps: ['speed_boost'],
|
|
101
|
+
})
|
|
69
102
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
103
|
+
// Load previously saved state
|
|
104
|
+
const gameState = await client.games.loadState()
|
|
105
|
+
console.log('Loaded state:', gameState)
|
|
106
|
+
|
|
107
|
+
// Exit game (automatically ends session if managed)
|
|
108
|
+
await client.runtime.exit()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### User & Inventory Management
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Get user information
|
|
115
|
+
const user = await client.users.me()
|
|
116
|
+
|
|
117
|
+
// Inventory operations (accepts UUIDs or slugs)
|
|
118
|
+
const inventory = await client.users.inventory.get()
|
|
119
|
+
await client.users.inventory.add('magic-sword', 1)
|
|
120
|
+
await client.users.inventory.remove('health-potion', 1)
|
|
121
|
+
|
|
122
|
+
// Check item quantities and ownership
|
|
123
|
+
const goldCount = await client.users.inventory.quantity('gold-coin')
|
|
124
|
+
const hasKey = await client.users.inventory.has('dungeon-key')
|
|
125
|
+
const hasEnoughGold = await client.users.inventory.has('gold-coin', 100)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Credits & Currency
|
|
75
129
|
|
|
76
|
-
|
|
77
|
-
|
|
130
|
+
```typescript
|
|
131
|
+
// Platform currency management
|
|
132
|
+
const balance = await client.credits.balance()
|
|
133
|
+
await client.credits.add(100)
|
|
134
|
+
await client.credits.spend(50)
|
|
135
|
+
|
|
136
|
+
// Check affordability
|
|
137
|
+
if ((await client.credits.balance()) >= 100) {
|
|
138
|
+
await client.credits.spend(100)
|
|
139
|
+
console.log('Purchase successful!')
|
|
78
140
|
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Experience & Levels
|
|
79
144
|
|
|
80
|
-
|
|
145
|
+
```typescript
|
|
146
|
+
// Level management
|
|
147
|
+
const userLevel = await client.levels.get()
|
|
148
|
+
const progress = await client.levels.progress()
|
|
149
|
+
console.log(`Level ${userLevel.currentLevel}, ${progress.xpToNextLevel} XP to next level`)
|
|
150
|
+
|
|
151
|
+
// Note: XP is now managed entirely through TimeBack integration.
|
|
152
|
+
// XP updates come from TimeBack webhooks only.
|
|
81
153
|
```
|
|
82
154
|
|
|
83
|
-
|
|
155
|
+
### Real-time Communication
|
|
84
156
|
|
|
85
|
-
|
|
86
|
-
import { initFromWindow } from '@playcademy/sdk'
|
|
157
|
+
Open a direct, low-latency communication channel for your game, perfect for multiplayer interactions.
|
|
87
158
|
|
|
88
|
-
|
|
89
|
-
|
|
159
|
+
```typescript
|
|
160
|
+
// Open a channel scoped to the current game
|
|
161
|
+
const channel = await client.realtime.open('lobby-chat')
|
|
90
162
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
console.log('Player inventory:', rewards)
|
|
99
|
-
|
|
100
|
-
// 3) Save game state (uses client.gameId implicitly)
|
|
101
|
-
await client.games.saveState({
|
|
102
|
-
currentLevel: 'level_2', // This is separate from progress, often more transient
|
|
103
|
-
health: 95,
|
|
104
|
-
})
|
|
105
|
-
console.log('Game state saved')
|
|
163
|
+
// Send messages to other players in the channel
|
|
164
|
+
channel.send({ type: 'chat', message: 'Hello, world!' })
|
|
165
|
+
|
|
166
|
+
// Listen for incoming messages
|
|
167
|
+
channel.onMessage(data => {
|
|
168
|
+
console.log('Received message:', data)
|
|
169
|
+
})
|
|
106
170
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
171
|
+
// Remember to close the channel when it's no longer needed
|
|
172
|
+
// channel.close()
|
|
173
|
+
```
|
|
110
174
|
|
|
111
|
-
|
|
112
|
-
await client.games.endSession(sessionId)
|
|
113
|
-
console.log('Session ended')
|
|
175
|
+
## API Reference
|
|
114
176
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
177
|
+
### Core Modules
|
|
178
|
+
|
|
179
|
+
#### **Authentication** (`client.auth`)
|
|
180
|
+
|
|
181
|
+
- `logout()`: Logs out user and clears authentication token
|
|
182
|
+
|
|
183
|
+
#### **Users** (`client.users`)
|
|
184
|
+
|
|
185
|
+
- `me()`: Get current user information
|
|
186
|
+
- **Inventory** (`client.users.inventory`):
|
|
187
|
+
- `get()`: Get user's inventory
|
|
188
|
+
- `add(identifier, quantity)`: Add items to inventory
|
|
189
|
+
- `remove(identifier, quantity)`: Remove items from inventory
|
|
190
|
+
- `quantity(identifier)`: Get item quantity
|
|
191
|
+
- `has(identifier, minQuantity?)`: Check item ownership
|
|
192
|
+
|
|
193
|
+
#### **Games** (`client.games`)
|
|
194
|
+
|
|
195
|
+
- `list()`: Get all available games
|
|
196
|
+
- `fetch(gameIdOrSlug)`: Get specific game details
|
|
197
|
+
- `saveState(state)`: Save transient game state
|
|
198
|
+
- `loadState()`: Load saved game state
|
|
199
|
+
- `startSession(gameId?)`: Start game session
|
|
200
|
+
- `endSession(sessionId, gameId?)`: End game session
|
|
201
|
+
|
|
202
|
+
#### **Credits** (`client.credits`)
|
|
203
|
+
|
|
204
|
+
- `balance()`: Get current credits balance
|
|
205
|
+
- `add(amount)`: Add credits to user
|
|
206
|
+
- `spend(amount)`: Spend user credits
|
|
207
|
+
|
|
208
|
+
#### **Levels** (`client.levels`)
|
|
209
|
+
|
|
210
|
+
- `get()`: Get current user level information
|
|
211
|
+
- `progress()`: Get level progress and XP to next level
|
|
212
|
+
- `addXP(amount)`: Add experience points
|
|
213
|
+
- **Config** (`client.levels.config`):
|
|
214
|
+
- `list()`: Get all level configurations
|
|
215
|
+
- `get(level)`: Get specific level configuration
|
|
216
|
+
|
|
217
|
+
#### **Maps** (`client.maps`)
|
|
218
|
+
|
|
219
|
+
- `elements(mapId)`: Get map elements and points of interest
|
|
220
|
+
|
|
221
|
+
#### **Runtime** (`client.runtime`)
|
|
222
|
+
|
|
223
|
+
- `getGameToken(gameId, options?)`: Get game-specific authentication token
|
|
224
|
+
- `exit()`: Signal platform to exit game view
|
|
225
|
+
|
|
226
|
+
#### **Real-time** (`client.realtime`)
|
|
227
|
+
|
|
228
|
+
- `open(channelName?)`: Opens a game-scoped WebSocket channel.
|
|
229
|
+
- `token.get()`: Retrieves a JWT for the real-time service.
|
|
230
|
+
|
|
231
|
+
#### **Leaderboard** (`client.leaderboard`) - Game-specific
|
|
232
|
+
|
|
233
|
+
- `fetch(options?)`: Get leaderboard for a specific game
|
|
234
|
+
- `options.timeframe`: Filter by time period (`'all_time'`, `'monthly'`, `'weekly'`, `'daily'`)
|
|
235
|
+
- `options.gameId`: Game ID to fetch leaderboard for (required)
|
|
236
|
+
- `options.limit`: Number of entries to return (default: 10)
|
|
237
|
+
- `options.offset`: Pagination offset (default: 0)
|
|
238
|
+
|
|
239
|
+
#### **Scores** (`client.scores`) - Platform-wide
|
|
240
|
+
|
|
241
|
+
- `submit(gameId, score, metadata?)`: Submit a score for any game
|
|
242
|
+
- `getUserScores(userId, options?)`: Get all scores for a user
|
|
243
|
+
- `options.gameId`: Filter by specific game (optional)
|
|
244
|
+
- `options.limit`: Number of scores to return (default: 50)
|
|
245
|
+
|
|
246
|
+
### Developer Tools
|
|
247
|
+
|
|
248
|
+
#### **Developer Authentication** (`client.dev.auth`)
|
|
249
|
+
|
|
250
|
+
- `applyForDeveloper()`: Apply for developer status
|
|
251
|
+
- `getDeveloperStatus()`: Check current developer status
|
|
252
|
+
|
|
253
|
+
#### **Game Management** (`client.dev.games`)
|
|
254
|
+
|
|
255
|
+
- `upsert(slug, metadata, gameFile)`: Create or update game
|
|
256
|
+
- `update(gameId, updates)`: Update game properties
|
|
257
|
+
- `delete(gameId)`: Delete game
|
|
258
|
+
|
|
259
|
+
#### **API Keys** (`client.dev.keys`)
|
|
260
|
+
|
|
261
|
+
- `createKey(gameId, name)`: Create API key for server authentication
|
|
262
|
+
- `listKeys()`: List all API keys
|
|
263
|
+
- `revokeKey(keyId)`: Revoke API key
|
|
264
|
+
|
|
265
|
+
#### **Item Management** (`client.dev.items`)
|
|
266
|
+
|
|
267
|
+
- `list(gameId)`: List all items for a game
|
|
268
|
+
- `get(gameId, slug)`: Get specific item
|
|
269
|
+
- `create(gameId, slug, data)`: Create new game item
|
|
270
|
+
- `update(gameId, itemId, updates)`: Update existing item
|
|
271
|
+
- `delete(gameId, itemId)`: Delete item
|
|
272
|
+
|
|
273
|
+
## Event System
|
|
274
|
+
|
|
275
|
+
The SDK provides real-time event notifications for important platform changes:
|
|
276
|
+
|
|
277
|
+
### Available Events
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
// Authentication changes
|
|
281
|
+
client.on('authChange', payload => {
|
|
282
|
+
console.log('Authentication changed:', payload.token)
|
|
283
|
+
})
|
|
284
|
+
|
|
285
|
+
// Inventory changes
|
|
286
|
+
client.on('inventoryChange', payload => {
|
|
287
|
+
console.log(`Item ${payload.itemId}: ${payload.delta} (total: ${payload.newTotal})`)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
// Experience gained
|
|
291
|
+
client.on('xpGained', payload => {
|
|
292
|
+
console.log(`Gained ${payload.amount} XP (total: ${payload.totalXP})`)
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
// Level up notifications
|
|
296
|
+
client.on('levelUp', payload => {
|
|
297
|
+
console.log(`Level up! ${payload.oldLevel} → ${payload.newLevel}`)
|
|
298
|
+
console.log('Credits awarded:', payload.creditsAwarded)
|
|
299
|
+
})
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Event-Driven UI Updates
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Update UI in response to platform events
|
|
306
|
+
client.on('inventoryChange', payload => {
|
|
307
|
+
updateInventoryDisplay(payload.itemId, payload.newTotal)
|
|
308
|
+
})
|
|
309
|
+
|
|
310
|
+
client.on('levelUp', payload => {
|
|
311
|
+
showLevelUpAnimation(payload.newLevel)
|
|
312
|
+
showCreditsAwarded(payload.creditsAwarded)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
client.on('xpGained', payload => {
|
|
316
|
+
updateXPBar(payload.totalXP, payload.leveledUp)
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Advanced Usage
|
|
321
|
+
|
|
322
|
+
### Manual Initialization
|
|
323
|
+
|
|
324
|
+
For server-side applications or custom environments:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
import { PlaycademyClient } from '@playcademy/sdk'
|
|
328
|
+
|
|
329
|
+
import type { LoginResponse } from '@playcademy/sdk'
|
|
330
|
+
|
|
331
|
+
// Step 1: Authenticate
|
|
332
|
+
const loginData: LoginResponse = await PlaycademyClient.login(
|
|
333
|
+
'https://api.playcademy.com',
|
|
334
|
+
'user@example.com',
|
|
335
|
+
'password',
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
// Step 2: Initialize client
|
|
339
|
+
const client = new PlaycademyClient({
|
|
340
|
+
baseUrl: 'https://api.playcademy.com',
|
|
341
|
+
token: loginData.token,
|
|
342
|
+
gameId: 'your-game-id', // Optional: enables automatic session management
|
|
343
|
+
})
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### Custom Configuration
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
const client = new PlaycademyClient({
|
|
350
|
+
baseUrl: 'https://api.playcademy.com',
|
|
351
|
+
token: 'your-auth-token',
|
|
352
|
+
gameId: 'your-game-id',
|
|
353
|
+
// Additional options
|
|
354
|
+
timeout: 10000, // Request timeout in milliseconds
|
|
355
|
+
retries: 3, // Number of retry attempts
|
|
356
|
+
})
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Error Handling
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { PlaycademyError } from '@playcademy/sdk'
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
const user = await client.users.me()
|
|
366
|
+
// Handle success
|
|
367
|
+
} catch (error) {
|
|
368
|
+
if (error instanceof PlaycademyError) {
|
|
369
|
+
console.error('Playcademy API Error:', error.message)
|
|
370
|
+
console.error('Status Code:', error.statusCode)
|
|
371
|
+
console.error('Error Code:', error.code)
|
|
372
|
+
} else {
|
|
373
|
+
console.error('Unexpected error:', error)
|
|
119
374
|
}
|
|
120
375
|
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Development Environment
|
|
379
|
+
|
|
380
|
+
### Integration with Playcademy Vite Plugin
|
|
381
|
+
|
|
382
|
+
When using the official Playcademy Vite templates, the development environment is automatically configured:
|
|
383
|
+
|
|
384
|
+
```typescript
|
|
385
|
+
// In your game's main file
|
|
386
|
+
import { PlaycademyClient } from '@playcademy/sdk'
|
|
387
|
+
|
|
388
|
+
// The vite plugin automatically starts the sandbox
|
|
389
|
+
const client = await PlaycademyClient.init()
|
|
390
|
+
// SDK automatically connects to local sandbox at http://localhost:4321
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Manual Sandbox Setup
|
|
394
|
+
|
|
395
|
+
If not using the Vite plugin, start the sandbox manually:
|
|
396
|
+
|
|
397
|
+
```bash
|
|
398
|
+
# Start sandbox server (will also start realtime server on port 4322)
|
|
399
|
+
bunx @playcademy/sandbox --port 4321 --verbose
|
|
400
|
+
|
|
401
|
+
# In your application
|
|
402
|
+
const client = new PlaycademyClient({
|
|
403
|
+
baseUrl: 'http://localhost:4321/api',
|
|
404
|
+
realtimeUrl: 'ws://localhost:4322',
|
|
405
|
+
token: 'dev-token' // Sandbox provides mock authentication
|
|
406
|
+
})
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Best Practices
|
|
410
|
+
|
|
411
|
+
### Initialization & Setup
|
|
412
|
+
|
|
413
|
+
- **Always use automatic initialization** for game development with `PlaycademyClient.init()`
|
|
414
|
+
- **Handle initialization errors gracefully** with proper try-catch blocks
|
|
415
|
+
- **Store the client instance** for reuse throughout your application lifecycle
|
|
416
|
+
|
|
417
|
+
### State Management
|
|
418
|
+
|
|
419
|
+
- **Use `games.saveState()`** for transient data (current level, position, temporary status)
|
|
420
|
+
- **Use `users.inventory`** for persistent items and resources that carry between sessions
|
|
421
|
+
- **Save state periodically**, not on every frame or minor change
|
|
422
|
+
- **Load state once** at game start, then manage locally
|
|
423
|
+
|
|
424
|
+
### Performance Optimization
|
|
425
|
+
|
|
426
|
+
- **Cache frequently accessed data** like user information and inventory
|
|
427
|
+
- **Batch inventory operations** when possible instead of individual API calls
|
|
428
|
+
- **Use event listeners** to update UI reactively rather than polling
|
|
429
|
+
- **Implement proper loading states** for better user experience
|
|
430
|
+
|
|
431
|
+
### Error Handling
|
|
432
|
+
|
|
433
|
+
- **Wrap all SDK calls** in appropriate try-catch blocks
|
|
434
|
+
- **Provide fallback behavior** for network errors and API failures
|
|
435
|
+
- **Show meaningful error messages** to users when operations fail
|
|
436
|
+
- **Implement retry logic** for non-critical operations
|
|
437
|
+
|
|
438
|
+
### Development Workflow
|
|
121
439
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
440
|
+
- **Use the sandbox environment** for all local development
|
|
441
|
+
- **Test both online and offline scenarios** to ensure robust error handling
|
|
442
|
+
- **Enable verbose logging** during development for debugging
|
|
443
|
+
- **Validate API responses** and handle edge cases appropriately
|
|
444
|
+
|
|
445
|
+
## Testing
|
|
446
|
+
|
|
447
|
+
### Unit Testing
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Mock the SDK for unit tests
|
|
451
|
+
import { jest } from '@jest/globals'
|
|
452
|
+
|
|
453
|
+
// Mock the entire SDK module
|
|
454
|
+
jest.mock('@playcademy/sdk', () => ({
|
|
455
|
+
PlaycademyClient: {
|
|
456
|
+
init: jest.fn().mockResolvedValue({
|
|
457
|
+
users: {
|
|
458
|
+
me: jest.fn().mockResolvedValue({ id: 'test-user', name: 'Test User' }),
|
|
459
|
+
inventory: {
|
|
460
|
+
get: jest.fn().mockResolvedValue([]),
|
|
461
|
+
add: jest.fn().mockResolvedValue(undefined),
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
}),
|
|
465
|
+
},
|
|
466
|
+
}))
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Integration Testing
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
// Test with real sandbox
|
|
473
|
+
import { PlaycademyClient } from '@playcademy/sdk'
|
|
474
|
+
|
|
475
|
+
describe('Playcademy Integration', () => {
|
|
476
|
+
let client: PlaycademyClient
|
|
477
|
+
|
|
478
|
+
beforeAll(async () => {
|
|
479
|
+
// Initialize with sandbox
|
|
480
|
+
client = new PlaycademyClient({
|
|
481
|
+
baseUrl: 'http://localhost:4321/api',
|
|
482
|
+
token: 'test-token',
|
|
483
|
+
})
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
test('should fetch user data', async () => {
|
|
487
|
+
const user = await client.users.me()
|
|
488
|
+
expect(user).toBeDefined()
|
|
489
|
+
expect(user.name).toEqual(expect.any(String))
|
|
490
|
+
})
|
|
491
|
+
})
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
## Troubleshooting
|
|
495
|
+
|
|
496
|
+
### Common Issues
|
|
497
|
+
|
|
498
|
+
**SDK Initialization Timeout**
|
|
499
|
+
|
|
500
|
+
```
|
|
501
|
+
Error: PLAYCADEMY_INIT not received within 5000ms
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
- Ensure you're running in the correct environment (development with sandbox, or production with platform)
|
|
505
|
+
- Check that the Vite plugin is properly configured
|
|
506
|
+
- Verify the sandbox is running on the expected port
|
|
507
|
+
|
|
508
|
+
**Authentication Errors**
|
|
509
|
+
|
|
510
|
+
```
|
|
511
|
+
Error: Unauthorized (401)
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
- Check that your authentication token is valid
|
|
515
|
+
- Ensure you have the necessary permissions for the operation
|
|
516
|
+
- Try re-authenticating with `PlaycademyClient.login()`
|
|
517
|
+
|
|
518
|
+
**Network Connection Issues**
|
|
519
|
+
|
|
520
|
+
```
|
|
521
|
+
Error: Failed to fetch
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
- Verify the API endpoint is accessible
|
|
525
|
+
- Check network connectivity
|
|
526
|
+
- Ensure CORS is properly configured for cross-origin requests
|
|
527
|
+
|
|
528
|
+
### Debugging
|
|
529
|
+
|
|
530
|
+
Use these debugging techniques for troubleshooting SDK issues:
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
// Check initialization process
|
|
534
|
+
try {
|
|
535
|
+
const client = await PlaycademyClient.init()
|
|
536
|
+
console.log('SDK initialized successfully')
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.error('SDK initialization failed:', error)
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
// Monitor network requests in browser dev tools (Network tab)
|
|
542
|
+
// Check console for SDK error messages
|
|
543
|
+
// Verify API responses and error details
|
|
544
|
+
```
|
|
162
545
|
|
|
163
546
|
## Contributing
|
|
164
547
|
|
|
165
|
-
|
|
548
|
+
The SDK is a critical component of the Playcademy platform ecosystem. When contributing:
|
|
166
549
|
|
|
167
|
-
|
|
550
|
+
1. **Maintain Type Safety**: Ensure all new APIs are fully typed
|
|
551
|
+
2. **Update Documentation**: Keep this README and JSDoc comments current
|
|
552
|
+
3. **Add Tests**: Include both unit and integration tests for new features
|
|
553
|
+
4. **Follow Patterns**: Use consistent patterns with existing SDK methods
|
|
554
|
+
5. **Handle Errors**: Implement proper error handling and user feedback
|
|
168
555
|
|
|
169
|
-
|
|
556
|
+
For general contribution guidelines, see the [monorepo CONTRIBUTING.md](../../CONTRIBUTING.md).
|