@arcadible/sdk 1.2.0 → 1.2.2
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 +7 -10
- package/dist/saves/backend/indexeddb.d.ts +1 -0
- package/dist/saves/backend/indexeddb.js +19 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,34 +27,31 @@ Each build of a game on [Arcadible](https://arcadible.com) has its own origin, s
|
|
|
27
27
|
npm install @arcadible/sdk
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
```html
|
|
33
|
-
<script src="/arcadible/sdk/v1.js"></script>
|
|
34
|
-
```
|
|
30
|
+
The package is for a game with a build step, such as a Vite project. A game without one uses the [hosted script](https://developer.arcadible.com/references/arcadible-sdk#install) instead.
|
|
35
31
|
|
|
36
32
|
## Save and load
|
|
37
33
|
|
|
38
34
|
```js
|
|
39
35
|
import { arcadible } from "@arcadible/sdk";
|
|
40
36
|
|
|
37
|
+
// at startup: once the saves have loaded, the player's progress, or a new game.
|
|
41
38
|
await arcadible.ready;
|
|
42
|
-
|
|
43
|
-
const saved = arcadible.saves.readSync("progress");
|
|
39
|
+
const saved = await arcadible.saves.read("progress");
|
|
44
40
|
const progress = saved ? JSON.parse(saved) : { level: 1 };
|
|
45
41
|
|
|
42
|
+
// at a checkpoint:
|
|
46
43
|
await arcadible.saves.write("progress", JSON.stringify(progress));
|
|
47
44
|
```
|
|
48
45
|
|
|
49
|
-
A save is a name holding one string. `ready` settles once the game's saves have loaded, and never fails; from then on
|
|
46
|
+
A save is a name holding one string. `arcadible.ready` settles once the game's saves have loaded, and never fails; calls made before it wait for it, and from then on `readSync`, `listSync`, and `infoSync` answer at once from memory, for reads during play. Every call that fails rejects (or, for the sync calls, throws) with an `ArcadibleError`, whose `code` says what to do. `arcadible.saves.where` says who can read the saves back: every version of the game, only this build, or only until the page closes.
|
|
50
47
|
|
|
51
48
|
Off Arcadible, such as on your dev server, the package works on its own, with the same rules and limits: saves are kept on the page's own origin, `arcadible.saves.where` is `"build"`, and `arcadible.build` is `null`.
|
|
52
49
|
|
|
53
50
|
## Learn more
|
|
54
51
|
|
|
55
|
-
- [
|
|
52
|
+
- [Arcadible SDK](https://developer.arcadible.com/references/arcadible-sdk): installing it, the hosted script, `ready` and `build`, and every code.
|
|
56
53
|
- [`arcadible.saves`](https://developer.arcadible.com/references/arcadible-sdk/saves): every call, what it returns, and when it fails.
|
|
57
|
-
- [
|
|
54
|
+
- [Saves](https://developer.arcadible.com/build/saves): using saves well, across versions, channels, and tabs, and where they're kept.
|
|
58
55
|
- [Limits](https://developer.arcadible.com/references/limits#saves): how many saves, and how large.
|
|
59
56
|
|
|
60
57
|
## License
|
|
@@ -13,6 +13,9 @@ const CHANNEL = "arcadible-saves";
|
|
|
13
13
|
// every save is a guest's for One; a signed-in player's sit beside them
|
|
14
14
|
// later, under their own id, with no migration.
|
|
15
15
|
const GUEST = "guest";
|
|
16
|
+
// after a failed reopen, how long calls fail at once before it's tried again,
|
|
17
|
+
// so a game saving every frame doesn't try, and fail, every frame.
|
|
18
|
+
const REOPEN_WAIT = 1000;
|
|
16
19
|
// [function]
|
|
17
20
|
function settle(request) {
|
|
18
21
|
return new Promise((resolve, reject) => {
|
|
@@ -66,7 +69,7 @@ function isClosed(err) {
|
|
|
66
69
|
// refuses to open (a browser blocking site data, some `file://` pages),
|
|
67
70
|
// they're kept in memory, and `status()` says so. Once it has opened, it
|
|
68
71
|
// never switches: a lost connection fails calls with `unavailable` until it
|
|
69
|
-
// opens again, rather than show an empty copy.
|
|
72
|
+
// opens again, trying at most once a second, rather than show an empty copy.
|
|
70
73
|
export function indexedDBBackend(options = {}) {
|
|
71
74
|
const factory = options.factory === undefined
|
|
72
75
|
? (globalThis.indexedDB ?? null)
|
|
@@ -79,6 +82,9 @@ export function indexedDBBackend(options = {}) {
|
|
|
79
82
|
let opened = false;
|
|
80
83
|
let current = null;
|
|
81
84
|
let opening = null;
|
|
85
|
+
// whether reopening has failed since it last worked, and when to try again.
|
|
86
|
+
let down = false;
|
|
87
|
+
let retryAt = 0;
|
|
82
88
|
let asked = false;
|
|
83
89
|
const keyOf = (scope) => `${player}\u0000${scope.game}\u0000${scope.channel}`;
|
|
84
90
|
const rangeOf = (scope) => IDBKeyRange.only([player, scope.game, scope.channel]);
|
|
@@ -86,6 +92,7 @@ export function indexedDBBackend(options = {}) {
|
|
|
86
92
|
if (current === db)
|
|
87
93
|
current = null;
|
|
88
94
|
};
|
|
95
|
+
const unreachable = () => new ArcadibleError(Codes.Unavailable, "Saves can't be reached right now. Try again.");
|
|
89
96
|
const connect = async function () {
|
|
90
97
|
try {
|
|
91
98
|
if (!factory)
|
|
@@ -103,13 +110,19 @@ export function indexedDBBackend(options = {}) {
|
|
|
103
110
|
globalThis.navigator?.storage?.persist?.().catch(() => false);
|
|
104
111
|
}
|
|
105
112
|
opened = true;
|
|
113
|
+
down = false;
|
|
106
114
|
current = db;
|
|
107
115
|
return db;
|
|
108
116
|
}
|
|
109
117
|
catch (err) {
|
|
110
118
|
if (opened) {
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
// once until it works again: the calls failing meanwhile say so.
|
|
120
|
+
if (!down) {
|
|
121
|
+
log("warn", "the saves' storage couldn't be opened again.", err);
|
|
122
|
+
}
|
|
123
|
+
down = true;
|
|
124
|
+
retryAt = Date.now() + (options.reopenWait ?? REOPEN_WAIT);
|
|
125
|
+
throw unreachable();
|
|
113
126
|
}
|
|
114
127
|
log("warn", "this browser won't keep saves here, so they're kept in memory until the page closes.", err);
|
|
115
128
|
status = "memory";
|
|
@@ -125,6 +138,9 @@ export function indexedDBBackend(options = {}) {
|
|
|
125
138
|
return Promise.resolve(null);
|
|
126
139
|
if (current)
|
|
127
140
|
return Promise.resolve(current);
|
|
141
|
+
if (down && !opening && Date.now() < retryAt) {
|
|
142
|
+
return Promise.reject(unreachable());
|
|
143
|
+
}
|
|
128
144
|
opening ??= connect();
|
|
129
145
|
return opening;
|
|
130
146
|
};
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.2.
|
|
1
|
+
export declare const VERSION = "1.2.2";
|
package/dist/version.js
CHANGED
package/package.json
CHANGED