@clocktone/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.
- package/README.md +402 -0
- package/android/build.gradle +69 -0
- package/android/src/main/kotlin/com/playloop/plugins/applovinmax/PlayLoopAppLovinMaxPlugin.kt +316 -0
- package/android/src/main/kotlin/com/playloop/plugins/deviceid/PlayLoopDeviceIdPlugin.kt +30 -0
- package/dist/loader/cjs/index.js +72 -0
- package/dist/loader/esm/index.js +69 -0
- package/dist/types/CashableSDK.d.ts +46 -0
- package/dist/types/adapters/CapacitorAdsAdapter.d.ts +25 -0
- package/dist/types/adapters/WebViewAdsAdapter.d.ts +13 -0
- package/dist/types/babylon/BabylonPlugin.d.ts +17 -0
- package/dist/types/babylon/index.d.ts +17 -0
- package/dist/types/index.d.ts +481 -0
- package/dist/types/loader/index.d.ts +300 -0
- package/dist/types/modules/AnalyticsModule.d.ts +17 -0
- package/dist/types/modules/EmbeddedAdsModule.d.ts +30 -0
- package/dist/types/modules/FeatureFlagModule.d.ts +36 -0
- package/dist/types/modules/RewardsModule.d.ts +19 -0
- package/dist/types/modules/SessionModule.d.ts +16 -0
- package/dist/types/modules/StandaloneAdsModule.d.ts +51 -0
- package/dist/types/transport/HttpTransport.d.ts +19 -0
- package/dist/types/transport/WebViewTransport.d.ts +17 -0
- package/dist/types/types.d.ts +147 -0
- package/dist/types/ui/UIModule.d.ts +64 -0
- package/docs/ads.md +210 -0
- package/docs/analytics.md +45 -0
- package/docs/babylon.md +88 -0
- package/docs/feature-flags.md +109 -0
- package/docs/game-integration-guide.md +449 -0
- package/docs/loader.md +113 -0
- package/docs/rewards.md +57 -0
- package/docs/session.md +43 -0
- package/docs/ui.md +248 -0
- package/docs/wire-protocol.md +194 -0
- package/package.json +81 -0
package/README.md
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
# @clocktone/game-sdk
|
|
2
|
+
|
|
3
|
+
Unified TypeScript SDK for PlayLoop games. Provides a single API surface for both **embedded** (WebView inside the PlayLoop app) and **standalone** (Capacitor-wrapped APK) game modes.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @clocktone/game-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
The npm package ships only the **CDN loader** — a ~1KB bootstrap that loads the full SDK from CDN at runtime. This keeps the SDK out of your bundle and enables over-the-air updates. See the [Game Integration Guide](docs/game-integration-guide.md) for a complete walkthrough.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { loadPlayLoopSDK } from '@clocktone/game-sdk/loader';
|
|
17
|
+
import type { PlayLoopSDKConfig } from '@clocktone/game-sdk/loader';
|
|
18
|
+
|
|
19
|
+
// Load SDK from CDN
|
|
20
|
+
const SDK = await loadPlayLoopSDK({ apiBaseUrl: 'https://api.vbackendapi.com' });
|
|
21
|
+
|
|
22
|
+
const sdk = new SDK({
|
|
23
|
+
gameId: 'smash-monsters',
|
|
24
|
+
debug: true,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
await sdk.init();
|
|
28
|
+
|
|
29
|
+
// Show a rewarded video ad
|
|
30
|
+
const success = await sdk.ads.showRewardedVideo();
|
|
31
|
+
if (success) {
|
|
32
|
+
await sdk.rewards.awardCoins();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Start play-time tracking
|
|
36
|
+
sdk.analytics.startAutoTracking();
|
|
37
|
+
|
|
38
|
+
// Clean up when done
|
|
39
|
+
sdk.dispose();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Babylon.js Integration
|
|
43
|
+
|
|
44
|
+
The Babylon.js plugin is auto-detected at runtime and binds your scene for automatic freeze/resume:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
// After SDK is loaded and initialized via the loader:
|
|
48
|
+
sdk.babylon.bindScene(scene, engine);
|
|
49
|
+
// The SDK will automatically stop/start the render loop
|
|
50
|
+
// when the host app freezes/resumes the game
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Modes
|
|
54
|
+
|
|
55
|
+
The SDK operates in two modes, auto-detected at runtime:
|
|
56
|
+
|
|
57
|
+
| | Embedded | Standalone |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| **Context** | WebView inside PlayLoop app | Capacitor-wrapped APK |
|
|
60
|
+
| **Detection** | `window.ReactNativeWebView` exists | `window.Capacitor` exists |
|
|
61
|
+
| **Transport** | postMessage bridge | HTTP REST calls |
|
|
62
|
+
| **Auth** | Host manages identity | App Set ID via Capacitor plugin |
|
|
63
|
+
| **Ads** | Host shows ads | Native AppLovin MAX plugin |
|
|
64
|
+
|
|
65
|
+
You can override detection by passing `mode` explicitly:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
const sdk = new PlayLoopSDK({
|
|
69
|
+
gameId: 'my-game',
|
|
70
|
+
mode: 'standalone',
|
|
71
|
+
apiBaseUrl: 'https://api.vbackendapi.com',
|
|
72
|
+
appLovin: {
|
|
73
|
+
sdkKey: 'YOUR_SDK_KEY',
|
|
74
|
+
rewardedAdUnitId: 'YOUR_REWARDED_AD_UNIT_ID',
|
|
75
|
+
interstitialAdUnitId: 'YOUR_INTERSTITIAL_AD_UNIT_ID',
|
|
76
|
+
bannerAdUnitId: 'YOUR_BANNER_AD_UNIT_ID',
|
|
77
|
+
},
|
|
78
|
+
ui: {
|
|
79
|
+
onClose: () => Capacitor.Plugins.App.exitApp(),
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface PlayLoopSDKConfig {
|
|
88
|
+
/** Unique game identifier */
|
|
89
|
+
gameId: string;
|
|
90
|
+
/** Force a specific mode instead of auto-detecting */
|
|
91
|
+
mode?: 'embedded' | 'standalone';
|
|
92
|
+
/** Backend API base URL (standalone mode only) */
|
|
93
|
+
apiBaseUrl?: string;
|
|
94
|
+
/** Request timeout in milliseconds (default: 10000) */
|
|
95
|
+
requestTimeoutMs?: number;
|
|
96
|
+
/** Enable debug logging (default: false) */
|
|
97
|
+
debug?: boolean;
|
|
98
|
+
/** AppLovin MAX config (required for standalone mode ads) */
|
|
99
|
+
appLovin?: {
|
|
100
|
+
/** SDK key from AppLovin dashboard (Account > General > Keys) */
|
|
101
|
+
sdkKey: string;
|
|
102
|
+
rewardedAdUnitId: string;
|
|
103
|
+
interstitialAdUnitId: string;
|
|
104
|
+
/** Optional - omit to disable banner ads */
|
|
105
|
+
bannerAdUnitId?: string;
|
|
106
|
+
};
|
|
107
|
+
/** UI config for standalone mode */
|
|
108
|
+
ui?: {
|
|
109
|
+
onClose?: () => void; // Close button callback
|
|
110
|
+
onMenuOpen?: () => void; // Menu button callback (optional)
|
|
111
|
+
showPlayLoopButton?: boolean; // Show floating panel button (default: false)
|
|
112
|
+
playLoopButton?: {
|
|
113
|
+
position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
> **Note**: The `appLovin` and `ui` configs are only used in standalone mode. In embedded mode, the host PlayLoop app handles ads and UI.
|
|
120
|
+
|
|
121
|
+
## Modules
|
|
122
|
+
|
|
123
|
+
The SDK exposes its functionality through focused modules:
|
|
124
|
+
|
|
125
|
+
| Module | Accessor | Description |
|
|
126
|
+
|---|---|---|
|
|
127
|
+
| [Session](docs/session.md) | `sdk.session` | User identity, balance |
|
|
128
|
+
| [Rewards](docs/rewards.md) | `sdk.rewards` | Coin awarding |
|
|
129
|
+
| [Ads](docs/ads.md) | `sdk.ads` | Rewarded video, interstitial & banner ads |
|
|
130
|
+
| [Analytics](docs/analytics.md) | `sdk.analytics` | Play-time tracking, theme color |
|
|
131
|
+
| [Babylon](docs/babylon.md) | `sdk.babylon` | Babylon.js render loop freeze/resume |
|
|
132
|
+
| [Feature Flags](docs/feature-flags.md) | `sdk.featureFlags` | Remote config, feature gates, event logging (Statsig) |
|
|
133
|
+
| [UI](docs/ui.md) | `sdk.ui` | Floating panel, sponsored modal, offers, tier celebrations (standalone only) |
|
|
134
|
+
|
|
135
|
+
## Authentication
|
|
136
|
+
|
|
137
|
+
**Embedded mode**: The host PlayLoop app handles all authentication. The SDK is always considered authenticated (`sdk.isAuthenticated === true`).
|
|
138
|
+
|
|
139
|
+
**Standalone mode**: The SDK reads the device's App Set ID via a Capacitor plugin and sends it as `X-Install-Token` header. The backend maps this to a known user if the user has previously opened the game in the PlayLoop app. If no mapping exists, `sdk.isAuthenticated` is `false` and coin-related features will fail gracefully.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
if (sdk.isAuthenticated) {
|
|
143
|
+
const { coins } = await sdk.session.getBalance();
|
|
144
|
+
console.log(`User has ${coins} coins`);
|
|
145
|
+
} else {
|
|
146
|
+
console.log('User not linked - coin features unavailable');
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Lifecycle Events
|
|
151
|
+
|
|
152
|
+
```typescript
|
|
153
|
+
// Called when the host app puts the game in the background
|
|
154
|
+
sdk.onFreeze(() => {
|
|
155
|
+
// Pause game logic, audio, etc.
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Called when the host app brings the game back
|
|
159
|
+
sdk.onResume(() => {
|
|
160
|
+
// Resume game logic, audio, etc.
|
|
161
|
+
});
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If you've bound a Babylon.js scene, the render loop is automatically stopped/started for you.
|
|
165
|
+
|
|
166
|
+
## Error Handling
|
|
167
|
+
|
|
168
|
+
SDK errors include a `code` property for programmatic handling:
|
|
169
|
+
|
|
170
|
+
| Code | Meaning |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `TIMEOUT` | Request timed out |
|
|
173
|
+
| `TRANSPORT_ERROR` | Communication layer failure |
|
|
174
|
+
| `AD_NOT_AVAILABLE` | No ad fill available |
|
|
175
|
+
| `NETWORK_ERROR` | HTTP request failed |
|
|
176
|
+
| `USER_NOT_LINKED` | Standalone user has no PlayLoop account mapping |
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
try {
|
|
180
|
+
await sdk.rewards.awardCoins();
|
|
181
|
+
} catch (err) {
|
|
182
|
+
if (err.code === 'USER_NOT_LINKED') {
|
|
183
|
+
// User hasn't opened this game in the PlayLoop app yet
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Cleanup
|
|
189
|
+
|
|
190
|
+
Always call `dispose()` when the SDK is no longer needed:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
sdk.dispose();
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This stops analytics tracking, removes event listeners, aborts pending requests, and cleans up the Babylon.js plugin.
|
|
197
|
+
|
|
198
|
+
## Top-Level Convenience APIs
|
|
199
|
+
|
|
200
|
+
### `PlayLoopSDK.getWidgetHeight(): number` (static)
|
|
201
|
+
|
|
202
|
+
Computes the widget top-bar height from `window.innerWidth` alone — no DOM or `init()` required. Call it at script load time to offset your game canvas immediately:
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
const height = PlayLoopSDK.getWidgetHeight();
|
|
206
|
+
game.setTopOffset(height);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Also available as a standalone function from both entry points:
|
|
210
|
+
|
|
211
|
+
```typescript
|
|
212
|
+
// CDN / main entry
|
|
213
|
+
import { getWidgetHeight } from '@clocktone/game-sdk';
|
|
214
|
+
|
|
215
|
+
// Loader entry (works before CDN script loads)
|
|
216
|
+
import { getWidgetHeight } from '@clocktone/game-sdk/loader';
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### `sdk.onWidgetReady(handler): () => void`
|
|
220
|
+
|
|
221
|
+
Fires after `init()` mounts the UI with the exact DOM-measured height. Use it to refine the initial estimate:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
sdk.onWidgetReady(({ height }) => {
|
|
225
|
+
game.setTopOffset(height); // update with real value
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### `sdk.getTopBarHeight(): number`
|
|
230
|
+
|
|
231
|
+
Returns the pixel height of the SDK's top bar. Before `init()` completes, falls back to `PlayLoopSDK.getWidgetHeight()` (computed estimate). After `init()`, returns the real DOM-measured value.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
const offset = sdk.getTopBarHeight();
|
|
235
|
+
gameCanvas.style.marginTop = `${offset}px`;
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### `sdk.adConfig: Readonly<AdConfig>`
|
|
239
|
+
|
|
240
|
+
Returns the current ad configuration with Statsig remote overrides merged on top of defaults. Shorthand for `sdk.featureFlags?.adConfig ?? DEFAULT_AD_CONFIG`.
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
const cooldown = sdk.adConfig.interstitialCooldownMs;
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
See [`AdConfig`](docs/feature-flags.md#adconfig-readonlyadconfig) for all available fields.
|
|
247
|
+
|
|
248
|
+
## Standalone UI
|
|
249
|
+
|
|
250
|
+
In standalone mode, when the user is authenticated, the SDK renders a **floating PlayLoopButton** that opens a panel with rewards info, cashout flow, and games. It also provides **sponsored video modals** for interstitials and **bonus/booster offer modals**.
|
|
251
|
+
|
|
252
|
+
See the full [UI Module documentation](docs/ui.md) for all methods, components, and behavior details.
|
|
253
|
+
|
|
254
|
+
### PlayLoopButton Panel
|
|
255
|
+
|
|
256
|
+
Shown automatically after `init()` when `showPlayLoopButton: true`. The panel has three content areas:
|
|
257
|
+
|
|
258
|
+
- **Rewards tab (main screen)** — coins pill, tier card with progress bar, "Convert coins to real money" CTA, partners section
|
|
259
|
+
- **Cashout screen (sub-screen)** — countdown timer, earnings display, cashout button, partners. Accessed via the CTA button; back button returns to main screen
|
|
260
|
+
- **Games tab** — lists available games
|
|
261
|
+
|
|
262
|
+
The panel trigger row displays the current coin balance with earn animations, inline countdown timer, and offer countdown badges.
|
|
263
|
+
|
|
264
|
+
### Sponsored Interstitial
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// Show a 3-second countdown modal, then the interstitial ad
|
|
268
|
+
const success = await sdk.ui?.showSponsoredInterstitial();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Shows an animated countdown, then the ad, then a success/failure state. Coins are automatically awarded on success with the current boost multiplier applied.
|
|
272
|
+
|
|
273
|
+
### Bonus & Booster Offers
|
|
274
|
+
|
|
275
|
+
The SDK periodically shows bonus coin offers and 2x boost offers via the `OfferScheduler`. When an offer triggers, a 20-second countdown badge appears on the panel trigger. If the user taps within the countdown, the corresponding modal opens (rewarded ad required to claim).
|
|
276
|
+
|
|
277
|
+
### Controlling the UI
|
|
278
|
+
|
|
279
|
+
```typescript
|
|
280
|
+
sdk.ui?.updateCoins(newBalance); // Manually update displayed coins
|
|
281
|
+
sdk.ui?.updateThemeColor('#1a1a2e'); // Change panel background
|
|
282
|
+
await sdk.ui?.refreshWallet(); // Re-fetch wallet data from backend
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
> `sdk.ui` is `null` in embedded mode or when the user is not authenticated.
|
|
286
|
+
|
|
287
|
+
## Testing on Device
|
|
288
|
+
|
|
289
|
+
The SDK ships with two Android test targets for on-device testing. Both reference the SDK as a local `file:` dependency, so they always pick up the latest build.
|
|
290
|
+
|
|
291
|
+
| Target | Directory | App ID | Description |
|
|
292
|
+
|--------|-----------|--------|-------------|
|
|
293
|
+
| **test-app** | `debug/` | `com.playloop.monster` | Minimal SDK debug harness |
|
|
294
|
+
| **test-game** | `debug/test-game/block-blast/Block-Blast/` | `com.playloop.monster` | Block Blast game with full SDK integration |
|
|
295
|
+
|
|
296
|
+
### Prerequisites
|
|
297
|
+
|
|
298
|
+
- Android device connected via USB with ADB debugging enabled
|
|
299
|
+
- Java 21 available (`JAVA_HOME=$(/usr/libexec/java_home -v 21)`)
|
|
300
|
+
|
|
301
|
+
### Quick Start
|
|
302
|
+
|
|
303
|
+
```bash
|
|
304
|
+
# 1. Build the SDK
|
|
305
|
+
cd cashable-game-sdk && npm run build
|
|
306
|
+
|
|
307
|
+
# 2a. Deploy test-app (convenience script)
|
|
308
|
+
cd debug
|
|
309
|
+
JAVA_HOME=$(/usr/libexec/java_home -v 21) npm run run:android
|
|
310
|
+
|
|
311
|
+
# 2b. Deploy test-game (Block Blast)
|
|
312
|
+
cd debug/test-game/block-blast/Block-Blast
|
|
313
|
+
npm run build
|
|
314
|
+
npx cap sync android
|
|
315
|
+
cd android && JAVA_HOME=$(/usr/libexec/java_home -v 21) ./gradlew assembleDebug && cd ..
|
|
316
|
+
adb install -r android/app/build/outputs/apk/debug/app-debug.apk
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### APK Location
|
|
320
|
+
|
|
321
|
+
After building, the debug APK is at:
|
|
322
|
+
- **test-app**: `debug/android/app/build/outputs/apk/debug/app-debug.apk`
|
|
323
|
+
- **test-game**: `debug/test-game/block-blast/Block-Blast/android/app/build/outputs/apk/debug/app-debug.apk`
|
|
324
|
+
|
|
325
|
+
### Claude Code Skill
|
|
326
|
+
|
|
327
|
+
The `sdk-android-test` skill automates the full build-and-deploy flow. Invoke it with `/sdk-android-test` in Claude Code to build the SDK and deploy to a connected device.
|
|
328
|
+
|
|
329
|
+
## Versioning
|
|
330
|
+
|
|
331
|
+
This package has two independent version tracks:
|
|
332
|
+
|
|
333
|
+
| Version | File | What it tracks | When to bump |
|
|
334
|
+
|---------|------|---------------|--------------|
|
|
335
|
+
| **npm** (loader + types) | `package.json` `"version"` | Loader code, type declarations, native plugins | Loader logic or exported types change |
|
|
336
|
+
| **CDN** (SDK runtime) | `cdn-version.json` `"version"` | Full SDK IIFE bundle served from CDN | SDK runtime code changes |
|
|
337
|
+
|
|
338
|
+
The npm package ships only the **loader** (~1KB bootstrap) and **type declarations**. The full SDK runtime is built separately and deployed to CDN (`vbackendapi.com/sdk/v<CDN_VERSION>/playloop-sdk.js`). At runtime, the loader fetches the SDK version from the backend and loads the IIFE bundle from CDN.
|
|
339
|
+
|
|
340
|
+
A CDN deploy does not require an npm publish, and vice versa.
|
|
341
|
+
|
|
342
|
+
## Publishing
|
|
343
|
+
|
|
344
|
+
### Automated (CI)
|
|
345
|
+
|
|
346
|
+
Push a tag matching `sdk-v*` to trigger the GitHub Actions workflow (`.github/workflows/publish-sdk.yml`):
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Bump version in package.json and/or cdn-version.json first
|
|
350
|
+
cd cashable-game-sdk
|
|
351
|
+
npm version patch # or minor/major — updates package.json version
|
|
352
|
+
|
|
353
|
+
# Tag and push
|
|
354
|
+
git add -A && git commit -m "chore(sdk): bump to v0.8.6"
|
|
355
|
+
git tag sdk-v0.8.6
|
|
356
|
+
git push origin main --tags
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The workflow will:
|
|
360
|
+
1. Build the loader and CDN bundle
|
|
361
|
+
2. Publish the npm package (`@clocktone/game-sdk`) to the public registry
|
|
362
|
+
3. Upload the CDN bundle to R2 (`playloop-media/sdk/v<CDN_VERSION>/playloop-sdk.js`)
|
|
363
|
+
|
|
364
|
+
**Required GitHub secrets:** `NPM_TOKEN`, `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`
|
|
365
|
+
|
|
366
|
+
### Manual
|
|
367
|
+
|
|
368
|
+
```bash
|
|
369
|
+
cd cashable-game-sdk
|
|
370
|
+
npm run build
|
|
371
|
+
npm publish --access public # publish loader to npm
|
|
372
|
+
CLOUDFLARE_ACCOUNT_ID=<id> wrangler r2 object put \
|
|
373
|
+
playloop-media/sdk/v0.7.0/playloop-sdk.js \
|
|
374
|
+
--file dist/cdn/playloop-sdk.js \
|
|
375
|
+
--content-type "application/javascript" \
|
|
376
|
+
--remote # upload CDN bundle to R2
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### npm package
|
|
380
|
+
|
|
381
|
+
- **Registry:** https://www.npmjs.com/package/@clocktone/game-sdk
|
|
382
|
+
- **Contents:** Loader (~5KB), TypeScript types, Android Capacitor plugins, docs
|
|
383
|
+
- **No secrets or business logic** — all config is passed at runtime
|
|
384
|
+
|
|
385
|
+
### R2 CDN bundle
|
|
386
|
+
|
|
387
|
+
- **Bucket:** `playloop-media` (on the Mirko@playloop Cloudflare account)
|
|
388
|
+
- **Path:** `sdk/v<VERSION>/playloop-sdk.js`
|
|
389
|
+
- **Fallback URL:** `https://getplayloop.com/sdk/v0.8.5/playloop-sdk.js`
|
|
390
|
+
|
|
391
|
+
## API Reference
|
|
392
|
+
|
|
393
|
+
- [CDN Loader](docs/loader.md)
|
|
394
|
+
- [Game Integration Guide](docs/game-integration-guide.md)
|
|
395
|
+
- [Session Module](docs/session.md)
|
|
396
|
+
- [Rewards Module](docs/rewards.md)
|
|
397
|
+
- [Ads Module](docs/ads.md)
|
|
398
|
+
- [Analytics Module](docs/analytics.md)
|
|
399
|
+
- [Babylon.js Plugin](docs/babylon.md)
|
|
400
|
+
- [Feature Flags Module](docs/feature-flags.md)
|
|
401
|
+
- [UI Module](docs/ui.md)
|
|
402
|
+
- [Wire Protocol](docs/wire-protocol.md) (for host app implementors)
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
ext {
|
|
2
|
+
junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
|
|
3
|
+
androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
|
|
4
|
+
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
|
|
5
|
+
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
repositories {
|
|
10
|
+
google()
|
|
11
|
+
mavenCentral()
|
|
12
|
+
}
|
|
13
|
+
dependencies {
|
|
14
|
+
classpath 'com.android.tools.build:gradle:8.7.0'
|
|
15
|
+
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0'
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
apply plugin: 'com.android.library'
|
|
20
|
+
apply plugin: 'kotlin-android'
|
|
21
|
+
|
|
22
|
+
android {
|
|
23
|
+
namespace "com.playloop.plugins"
|
|
24
|
+
compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
|
|
25
|
+
|
|
26
|
+
defaultConfig {
|
|
27
|
+
minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 22
|
|
28
|
+
targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
|
|
29
|
+
versionCode 1
|
|
30
|
+
versionName "0.1.0"
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
buildTypes {
|
|
34
|
+
release {
|
|
35
|
+
minifyEnabled false
|
|
36
|
+
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
compileOptions {
|
|
41
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
42
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
kotlinOptions {
|
|
46
|
+
jvmTarget = '17'
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
repositories {
|
|
51
|
+
google()
|
|
52
|
+
mavenCentral()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
dependencies {
|
|
56
|
+
implementation project(':capacitor-android')
|
|
57
|
+
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
58
|
+
|
|
59
|
+
// AppLovin MAX SDK
|
|
60
|
+
implementation 'com.applovin:applovin-sdk:13.5.1'
|
|
61
|
+
|
|
62
|
+
// Google Play Services App Set ID
|
|
63
|
+
implementation 'com.google.android.gms:play-services-appset:16.1.0'
|
|
64
|
+
|
|
65
|
+
// Testing
|
|
66
|
+
testImplementation "junit:junit:$junitVersion"
|
|
67
|
+
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
68
|
+
androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
|
|
69
|
+
}
|