@oasiz/sdk 0.1.0 → 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 +267 -20
- package/package.json +14 -1
package/README.md
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
# @oasiz/sdk
|
|
2
2
|
|
|
3
|
-
Typed SDK for Oasiz
|
|
3
|
+
Typed SDK for integrating games with the Oasiz platform. Handles score submission, haptic feedback, cross-session state persistence, multiplayer room codes, and app lifecycle events for local development.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
npm install @oasiz/sdk
|
|
8
|
+
npm install @oasiz/sdk@^0.1.0
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Quick start
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
import {
|
|
14
|
+
import { oasiz } from "@oasiz/sdk";
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// 1. Emit score normalization config once on init
|
|
17
|
+
oasiz.emitScoreConfig({
|
|
17
18
|
anchors: [
|
|
18
19
|
{ raw: 30, normalized: 100 },
|
|
19
20
|
{ raw: 60, normalized: 300 },
|
|
@@ -22,21 +23,267 @@ oasis.emitScoreConfig({
|
|
|
22
23
|
],
|
|
23
24
|
});
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
// 2. Load persisted state at the start of each session
|
|
27
|
+
const state = oasiz.loadGameState();
|
|
28
|
+
let level = typeof state.level === "number" ? state.level : 1;
|
|
29
|
+
|
|
30
|
+
// 3. Save state at checkpoints
|
|
31
|
+
oasiz.saveGameState({ level, coins: 42 });
|
|
32
|
+
|
|
33
|
+
// 4. Trigger haptics on key events
|
|
34
|
+
oasiz.triggerHaptic("medium");
|
|
35
|
+
|
|
36
|
+
// 5. Submit score when the game ends
|
|
37
|
+
oasiz.submitScore(score);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Score
|
|
43
|
+
|
|
44
|
+
### `oasiz.submitScore(score: number)`
|
|
45
|
+
|
|
46
|
+
Submit the player's final score at game over. Call this exactly once per session, when the game ends. The platform handles leaderboard persistence — do not track high scores locally.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
private onGameOver(): void {
|
|
50
|
+
oasiz.submitScore(Math.floor(this.score));
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `score` must be a non-negative integer. Floats are floored automatically.
|
|
55
|
+
- Do not call on intermediate scores or level completions, only on final game over.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
### `oasiz.emitScoreConfig(config)`
|
|
60
|
+
|
|
61
|
+
Maps raw score values to the platform's normalized 0–1000 scale. Call once during initialization, not every frame.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
oasiz.emitScoreConfig({
|
|
65
|
+
anchors: [
|
|
66
|
+
{ raw: 10, normalized: 100 }, // beginner
|
|
67
|
+
{ raw: 30, normalized: 300 }, // good
|
|
68
|
+
{ raw: 75, normalized: 600 }, // great
|
|
69
|
+
{ raw: 200, normalized: 950 }, // godlike
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Anchor rules:**
|
|
75
|
+
- Exactly 4 anchors required.
|
|
76
|
+
- `raw` values must be strictly increasing.
|
|
77
|
+
- `normalized` values must end at exactly `950`.
|
|
78
|
+
- Choose thresholds based on realistic player skill bands.
|
|
79
|
+
|
|
80
|
+
**Practical guidance by game type:**
|
|
81
|
+
- Survival / time games → use seconds survived as `raw`
|
|
82
|
+
- Score accumulation games → use points as `raw`
|
|
83
|
+
- Puzzle games → use level reached or stars earned as `raw`
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Haptics
|
|
88
|
+
|
|
89
|
+
### `oasiz.triggerHaptic(type: HapticType)`
|
|
90
|
+
|
|
91
|
+
Trigger native haptic feedback. Always guard with the user's haptics setting.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
type HapticType = "light" | "medium" | "heavy" | "success" | "error";
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
| Type | When to use |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `"light"` | UI button taps, menu navigation, D-pad press |
|
|
100
|
+
| `"medium"` | Collecting items, standard collisions, scoring |
|
|
101
|
+
| `"heavy"` | Explosions, major impacts, screen shake |
|
|
102
|
+
| `"success"` | Level complete, new high score, achievement unlocked |
|
|
103
|
+
| `"error"` | Damage taken, game over, invalid action |
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// UI buttons — always light
|
|
107
|
+
button.addEventListener("click", () => {
|
|
108
|
+
oasiz.triggerHaptic("light");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
// Tiered hit feedback
|
|
112
|
+
private onBallHit(zone: "center" | "edge"): void {
|
|
113
|
+
if (this.settings.haptics) {
|
|
114
|
+
oasiz.triggerHaptic(zone === "center" ? "success" : "medium");
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Game over
|
|
119
|
+
private onGameOver(): void {
|
|
120
|
+
oasiz.submitScore(this.score);
|
|
121
|
+
if (this.settings.haptics) {
|
|
122
|
+
oasiz.triggerHaptic("error");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Haptics are throttled internally (50ms cooldown) to prevent spam.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Game state persistence
|
|
132
|
+
|
|
133
|
+
Persist cross-session data such as unlocked levels, inventory, or lifetime stats. State is stored per-user per-game in the Oasiz backend — available across devices and app reinstalls.
|
|
134
|
+
|
|
135
|
+
### `oasiz.loadGameState(): Record<string, unknown>`
|
|
136
|
+
|
|
137
|
+
Returns the player's saved state synchronously. Returns `{}` if no state has been saved yet. Call once at the start of the game.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
private initFromSavedState(): void {
|
|
141
|
+
const state = oasiz.loadGameState();
|
|
142
|
+
this.level = typeof state.level === "number" ? state.level : 1;
|
|
143
|
+
this.lifetimeHits = typeof state.lifetimeHits === "number" ? state.lifetimeHits : 0;
|
|
144
|
+
this.unlockedSkins = Array.isArray(state.unlockedSkins) ? state.unlockedSkins : [];
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Always validate the shape of loaded data — it may be `{}` on first play.
|
|
149
|
+
|
|
150
|
+
### `oasiz.saveGameState(state: Record<string, unknown>)`
|
|
151
|
+
|
|
152
|
+
Queues a debounced save. Saves are batched automatically — call freely at checkpoints without worrying about request spam.
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
// Save after each level completion
|
|
156
|
+
private onLevelComplete(): void {
|
|
157
|
+
this.level += 1;
|
|
158
|
+
oasiz.saveGameState({
|
|
159
|
+
level: this.level,
|
|
160
|
+
lifetimeHits: this.lifetimeHits,
|
|
161
|
+
unlockedSkins: this.unlockedSkins,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
29
164
|
```
|
|
30
165
|
|
|
31
|
-
|
|
166
|
+
**Rules:**
|
|
167
|
+
- State must be a plain JSON object (not an array or primitive).
|
|
168
|
+
- Do not use `localStorage` for cross-session progress — use `saveGameState` so data syncs across platforms.
|
|
169
|
+
- Do not store scores here — scores are submitted via `submitScore`.
|
|
170
|
+
|
|
171
|
+
### `oasiz.flushGameState()`
|
|
172
|
+
|
|
173
|
+
Forces an immediate write, bypassing the debounce. Use at important checkpoints like game over or before the page unloads.
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
private onGameOver(): void {
|
|
177
|
+
oasiz.saveGameState({ level: this.level, lifetimeHits: this.lifetimeHits });
|
|
178
|
+
oasiz.flushGameState(); // ensure it lands before the page closes
|
|
179
|
+
oasiz.submitScore(this.score);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Lifecycle
|
|
186
|
+
|
|
187
|
+
The platform dispatches lifecycle events when the app goes to the background or returns to the foreground. Subscribe to pause game loops and audio accordingly.
|
|
188
|
+
|
|
189
|
+
### `oasiz.onPause(callback: () => void): Unsubscribe`
|
|
190
|
+
### `oasiz.onResume(callback: () => void): Unsubscribe`
|
|
191
|
+
|
|
192
|
+
Both return an unsubscribe function.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
const offPause = oasiz.onPause(() => {
|
|
196
|
+
this.gameLoop.stop();
|
|
197
|
+
this.bgMusic.pause();
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
const offResume = oasiz.onResume(() => {
|
|
201
|
+
this.gameLoop.start();
|
|
202
|
+
this.bgMusic.play();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Clean up when the game is destroyed
|
|
206
|
+
offPause();
|
|
207
|
+
offResume();
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## Multiplayer
|
|
213
|
+
|
|
214
|
+
### `oasiz.shareRoomCode(code: string | null)`
|
|
215
|
+
|
|
216
|
+
Notify the platform of the active multiplayer room so friends can join via the invite system. Pass `null` when leaving a room.
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { insertCoin, getRoomCode } from "playroomkit";
|
|
220
|
+
import { oasiz } from "@oasiz/sdk";
|
|
221
|
+
|
|
222
|
+
await insertCoin({ skipLobby: true });
|
|
223
|
+
oasiz.shareRoomCode(getRoomCode());
|
|
224
|
+
|
|
225
|
+
// On disconnect
|
|
226
|
+
oasiz.shareRoomCode(null);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### Read-only injected values
|
|
230
|
+
|
|
231
|
+
These are populated by the platform before the game loads. Always check for `undefined` before using.
|
|
232
|
+
|
|
233
|
+
```ts
|
|
234
|
+
// The platform's internal game ID
|
|
235
|
+
const gameId = oasiz.gameId;
|
|
236
|
+
|
|
237
|
+
// Pre-filled room code for auto-joining a friend's session
|
|
238
|
+
if (oasiz.roomCode) {
|
|
239
|
+
await connectToRoom(oasiz.roomCode);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Player identity for multiplayer games
|
|
243
|
+
const name = oasiz.playerName;
|
|
244
|
+
const avatar = oasiz.playerAvatar;
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## Named exports
|
|
250
|
+
|
|
251
|
+
All methods are also available as named exports if you prefer not to use the `oasiz` namespace object:
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
import {
|
|
255
|
+
submitScore,
|
|
256
|
+
emitScoreConfig,
|
|
257
|
+
triggerHaptic,
|
|
258
|
+
loadGameState,
|
|
259
|
+
saveGameState,
|
|
260
|
+
flushGameState,
|
|
261
|
+
shareRoomCode,
|
|
262
|
+
onPause,
|
|
263
|
+
onResume,
|
|
264
|
+
getGameId,
|
|
265
|
+
getRoomCode,
|
|
266
|
+
getPlayerName,
|
|
267
|
+
getPlayerAvatar,
|
|
268
|
+
} from "@oasiz/sdk";
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## TypeScript types
|
|
274
|
+
|
|
275
|
+
```ts
|
|
276
|
+
import type { HapticType, ScoreConfig, ScoreAnchor, GameState } from "@oasiz/sdk";
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Local development
|
|
282
|
+
|
|
283
|
+
All methods safely no-op when the platform bridges are not injected. In development mode a console warning is logged so you know the call was made:
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
[oasiz/sdk] submitScore bridge is unavailable. This is expected in local development.
|
|
287
|
+
```
|
|
32
288
|
|
|
33
|
-
|
|
34
|
-
- `emitScoreConfig(config)`
|
|
35
|
-
- `triggerHaptic(type)`
|
|
36
|
-
- `loadGameState()`
|
|
37
|
-
- `saveGameState(state)`
|
|
38
|
-
- `flushGameState()`
|
|
39
|
-
- `shareRoomCode(code)`
|
|
40
|
-
- `onPause(callback)`
|
|
41
|
-
- `onResume(callback)`
|
|
42
|
-
- `oasis.gameId`, `oasis.roomCode`, `oasis.playerName`, `oasis.playerAvatar`
|
|
289
|
+
No crashes, no special setup required for local dev.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oasiz/sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Typed SDK for Oasiz game platform bridge APIs.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -28,9 +28,22 @@
|
|
|
28
28
|
"release": {
|
|
29
29
|
"branches": [
|
|
30
30
|
"main"
|
|
31
|
+
],
|
|
32
|
+
"plugins": [
|
|
33
|
+
"@semantic-release/commit-analyzer",
|
|
34
|
+
"@semantic-release/release-notes-generator",
|
|
35
|
+
"@semantic-release/github",
|
|
36
|
+
[
|
|
37
|
+
"@semantic-release/exec",
|
|
38
|
+
{
|
|
39
|
+
"prepareCmd": "npm version ${nextRelease.version} --no-git-tag-version --allow-same-version --no-workspaces",
|
|
40
|
+
"publishCmd": "npm publish --access public --no-workspaces"
|
|
41
|
+
}
|
|
42
|
+
]
|
|
31
43
|
]
|
|
32
44
|
},
|
|
33
45
|
"devDependencies": {
|
|
46
|
+
"@semantic-release/exec": "^7.1.0",
|
|
34
47
|
"@types/node": "^25.3.0",
|
|
35
48
|
"semantic-release": "^25.0.3",
|
|
36
49
|
"tsup": "^8.5.1",
|