@crowdedkingdoms/crowdyjs 17.12.0-dev.1 → 17.14.0-dev.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.
Files changed (70) hide show
  1. package/MIGRATION.md +127 -0
  2. package/README.md +99 -4
  3. package/dist/crowdy-dsh/bridge.d.ts.map +1 -1
  4. package/dist/crowdy-dsh/bridge.js +2 -0
  5. package/dist/crowdy-studio/controller.d.ts +64 -8
  6. package/dist/crowdy-studio/controller.d.ts.map +1 -1
  7. package/dist/crowdy-studio/controller.js +345 -46
  8. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  9. package/dist/crowdy-studio/dom-shell.js +16 -9
  10. package/dist/crowdy-studio/embed/hud-layer.d.ts +2 -1
  11. package/dist/crowdy-studio/embed/hud-layer.d.ts.map +1 -1
  12. package/dist/crowdy-studio/embed/panel.d.ts +9 -6
  13. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -1
  14. package/dist/crowdy-studio/embed/panel.js +4 -2
  15. package/dist/crowdy-studio/index.d.ts +2 -2
  16. package/dist/crowdy-studio/index.d.ts.map +1 -1
  17. package/dist/crowdy-studio/models.d.ts +1 -1
  18. package/dist/crowdy-studio/starter-projects.d.ts +22 -0
  19. package/dist/crowdy-studio/starter-projects.d.ts.map +1 -1
  20. package/dist/crowdy-studio/starter-projects.js +129 -5
  21. package/dist/domains/exec.d.ts +222 -20
  22. package/dist/domains/exec.d.ts.map +1 -1
  23. package/dist/domains/exec.js +215 -21
  24. package/dist/domains/marketplace.d.ts +15 -3
  25. package/dist/domains/marketplace.d.ts.map +1 -1
  26. package/dist/domains/marketplace.js +17 -3
  27. package/dist/errors.d.ts +3 -1
  28. package/dist/errors.d.ts.map +1 -1
  29. package/dist/generated/graphql.d.ts +422 -0
  30. package/dist/generated/graphql.d.ts.map +1 -1
  31. package/dist/generated/graphql.js +23 -11
  32. package/dist/grid-mods/exec-client-halves.d.ts +140 -0
  33. package/dist/grid-mods/exec-client-halves.d.ts.map +1 -0
  34. package/dist/grid-mods/exec-client-halves.js +341 -0
  35. package/dist/grid-mods/grid-host-calls.d.ts +3 -1
  36. package/dist/grid-mods/grid-host-calls.d.ts.map +1 -1
  37. package/dist/grid-mods/grid-host-calls.js +3 -1
  38. package/dist/grid-mods/index.d.ts +1 -0
  39. package/dist/grid-mods/index.d.ts.map +1 -1
  40. package/dist/grid-mods/index.js +1 -0
  41. package/dist/grid-mods/mod-runtime.d.ts +10 -2
  42. package/dist/grid-mods/mod-runtime.d.ts.map +1 -1
  43. package/dist/grid-mods/mod-runtime.js +2 -0
  44. package/dist/grid-scope.d.ts +1 -0
  45. package/dist/grid-scope.d.ts.map +1 -1
  46. package/dist/grid-scope.js +1 -0
  47. package/dist/index.d.ts +4 -4
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +4 -3
  50. package/dist/kit/npcs.d.ts.map +1 -1
  51. package/dist/kit/social.d.ts.map +1 -1
  52. package/dist/live-coding/rust-analysis.js +13 -1
  53. package/dist/player-runtime/glue-runtime.d.ts +19 -6
  54. package/dist/player-runtime/glue-runtime.d.ts.map +1 -1
  55. package/dist/player-runtime/glue-runtime.js +40 -16
  56. package/dist/player-runtime/player-code-broker.d.ts +29 -0
  57. package/dist/player-runtime/player-code-broker.d.ts.map +1 -1
  58. package/dist/player-runtime/player-code-broker.js +41 -8
  59. package/dist/player-runtime/player-glue-worker.d.ts +2 -1
  60. package/dist/player-runtime/player-glue-worker.d.ts.map +1 -1
  61. package/dist/player-runtime/player-glue-worker.js +3 -1
  62. package/dist/stores/actors.d.ts +12 -2
  63. package/dist/stores/actors.d.ts.map +1 -1
  64. package/dist/stores/actors.js +43 -18
  65. package/dist/stores/chunks.d.ts +12 -0
  66. package/dist/stores/chunks.d.ts.map +1 -1
  67. package/dist/stores/chunks.js +112 -39
  68. package/dist/stores/durable.d.ts.map +1 -1
  69. package/dist/stores/durable.js +2 -1
  70. package/package.json +1 -1
@@ -43,6 +43,10 @@ export function localStorageUuidStore(key = 'crowdyjs:actor-uuid') {
43
43
  },
44
44
  };
45
45
  }
46
+ function sendErrorCode(error) {
47
+ const code = error?.code;
48
+ return typeof code === 'string' && code !== '' ? code : 'SEND_FAILED';
49
+ }
46
50
  /**
47
51
  * The SDK-managed **local actor**: identity (minted + persisted uuid), typed
48
52
  * replication state, current chunk, an automatic 5 Hz send loop with
@@ -62,6 +66,8 @@ export class LocalActorStore {
62
66
  this.lastSentRecord = null;
63
67
  this.lastAckRecord = null;
64
68
  this.lastErrorRecord = null;
69
+ /** The last send failed before reaching the server: the next tick sends even if unchanged. */
70
+ this.resendDue = false;
65
71
  this.inFlight = new Map(); // seq → sentAt
66
72
  this.sequences = new SequenceAllocator();
67
73
  this.now = config.now ?? Date.now;
@@ -110,8 +116,9 @@ export class LocalActorStore {
110
116
  const doc = globalThis.document;
111
117
  if ((config.refreshOnVisibility ?? true) && doc?.addEventListener) {
112
118
  const onVisibility = () => {
113
- if (doc.visibilityState === 'visible')
114
- void this.refresh('visibility');
119
+ if (doc.visibilityState === 'visible') {
120
+ this.refresh('visibility').catch(() => { });
121
+ }
115
122
  };
116
123
  doc.addEventListener('visibilitychange', onVisibility);
117
124
  ctx.onDispose(() => doc.removeEventListener('visibilitychange', onVisibility));
@@ -190,23 +197,26 @@ export class LocalActorStore {
190
197
  return;
191
198
  await this.send(reason);
192
199
  }
193
- /** One send-loop tick: dedup unchanged state, keyframe when quiet. */
200
+ /**
201
+ * One send-loop tick: dedup unchanged state, keyframe when quiet. A failed
202
+ * loop send lands on {@link lastError} and the next tick sends again.
203
+ */
194
204
  tick() {
195
205
  if (!this.currentChunk)
196
206
  return;
197
207
  const encoded = this.config.codec.encode(this.currentState);
198
- if ((this.config.sendOnChange ?? true) && this.lastSentRecord) {
208
+ if ((this.config.sendOnChange ?? true) && this.lastSentRecord && !this.resendDue) {
199
209
  const quietFor = this.now() - this.lastSentRecord.sentAt;
200
210
  const keyframeEvery = this.config.keyframeEveryMs ?? 3000;
201
211
  if (encoded === this.lastSentRecord.encoded && quietFor < keyframeEvery) {
202
212
  return;
203
213
  }
204
214
  if (encoded === this.lastSentRecord.encoded) {
205
- void this.send('keyframe', encoded);
215
+ this.send('keyframe', encoded).catch(() => { });
206
216
  return;
207
217
  }
208
218
  }
209
- void this.send('interval', encoded);
219
+ this.send('interval', encoded).catch(() => { });
210
220
  }
211
221
  async send(reason, preEncoded) {
212
222
  const chunk = this.currentChunk;
@@ -217,7 +227,9 @@ export class LocalActorStore {
217
227
  const encoded = preEncoded ?? this.config.codec.encode(state);
218
228
  const sequenceNumber = this.sequences.next();
219
229
  const sentAt = this.now();
220
- this.lastSentRecord = { state, encoded, chunk, sequenceNumber, sentAt, reason };
230
+ const record = { state, encoded, chunk, sequenceNumber, sentAt, reason };
231
+ this.lastSentRecord = record;
232
+ this.resendDue = false;
221
233
  this.inFlight.set(sequenceNumber, sentAt);
222
234
  if (this.inFlight.size > 256) {
223
235
  const oldest = this.inFlight.keys().next().value;
@@ -231,17 +243,30 @@ export class LocalActorStore {
231
243
  uuid: this.uuid,
232
244
  detail: { reason },
233
245
  });
234
- await this.ctx.client.udp.sendActorUpdate({
235
- appId: this.ctx.appId,
236
- chunk,
237
- uuid: this.uuid,
238
- state: encoded,
239
- sequenceNumber,
240
- ...(this.config.distance !== undefined ? { distance: this.config.distance } : {}),
241
- ...(this.config.decayRate !== undefined
242
- ? { decayRate: this.config.decayRate }
243
- : {}),
244
- });
246
+ try {
247
+ await this.ctx.client.udp.sendActorUpdate({
248
+ appId: this.ctx.appId,
249
+ chunk,
250
+ uuid: this.uuid,
251
+ state: encoded,
252
+ sequenceNumber,
253
+ ...(this.config.distance !== undefined ? { distance: this.config.distance } : {}),
254
+ ...(this.config.decayRate !== undefined
255
+ ? { decayRate: this.config.decayRate }
256
+ : {}),
257
+ });
258
+ }
259
+ catch (error) {
260
+ this.inFlight.delete(sequenceNumber);
261
+ this.lastErrorRecord = {
262
+ errorCode: sendErrorCode(error),
263
+ sequenceNumber,
264
+ receivedAt: this.now(),
265
+ };
266
+ if (this.lastSentRecord === record)
267
+ this.resendDue = true;
268
+ throw error;
269
+ }
245
270
  }
246
271
  }
247
272
  /**
@@ -122,6 +122,9 @@ export declare class ChunkStore<TVoxelState = string, TChunkState = string> {
122
122
  private readonly config;
123
123
  private readonly chunks;
124
124
  private readonly inFlight;
125
+ private readonly hydrating;
126
+ /** Consecutive failed hydrations per chunk key. */
127
+ private readonly hydrateFailures;
125
128
  private readonly writeBackQueue;
126
129
  private readonly changeListeners;
127
130
  private readonly voxelStateCodec;
@@ -151,8 +154,17 @@ export declare class ChunkStore<TVoxelState = string, TChunkState = string> {
151
154
  * configured, marks server-unknown chunks `missing`, and seeds them via
152
155
  * `onMissing`. In-flight requests are deduped; safe to call every time the
153
156
  * player crosses a chunk boundary.
157
+ *
158
+ * Requests the platform refuses as busy are asked again with backoff, and
159
+ * at most 8 hydrations run at once. Hydration is best effort: a chunk whose
160
+ * states could not be fetched keeps the voxels the bulk load gave it
161
+ * (`hydrated` stays false) and is hydrated on a later call. A chunk whose
162
+ * bulk load failed is `failed` and is requested again by a later call. The
163
+ * promise rejects only when the bulk load itself fails.
154
164
  */
155
165
  ensureAround(center: ChunkCoord, radius: number): Promise<void>;
166
+ /** Hydrates chunks with at most {@link HYDRATE_CONCURRENCY} requests in flight. */
167
+ private hydrateAll;
156
168
  /**
157
169
  * Hydrate one chunk's sparse voxel states (and chunk state) via a
158
170
  * single-chunk fetch — bulk loads omit them.
@@ -1 +1 @@
1
- {"version":3,"file":"chunks.d.ts","sourceRoot":"","sources":["../../src/stores/chunks.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chunks.d.ts","sourceRoot":"","sources":["../../src/stores/chunks.ts"],"names":[],"mappings":"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"}
@@ -8,6 +8,33 @@
8
8
  import { generateCrowdyUuid, decodeBase64, encodeBase64 } from '../utils.js';
9
9
  import { rawCodec } from './codec.js';
10
10
  import { CHUNK_VOLUME, chunkDistance, chunkKey, chunksAround, fromChunkInput, toChunkInput, voxelIndex, } from './keys.js';
11
+ /**
12
+ * Hydrations in flight at once. Each is one request, and a bulk load returns hundreds of
13
+ * chunks: sent together, the API refuses a share of them as busy.
14
+ */
15
+ const HYDRATE_CONCURRENCY = 8;
16
+ /** Attempts for a request the platform refused before running it. */
17
+ const BUSY_ATTEMPTS = 4;
18
+ /** A chunk whose hydration failed this many times in a row is not asked for again. */
19
+ const HYDRATE_GIVE_UP = 3;
20
+ /** The platform refused the call before running it: asking again can succeed. */
21
+ function refusedBeforeRunning(error) {
22
+ const e = error;
23
+ const code = e?.code ?? e?.extensions?.code;
24
+ return code === 'PLATFORM_BUSY' || e?.extensions?.retryable === true;
25
+ }
26
+ async function whenNotBusy(call) {
27
+ for (let attempt = 1;; attempt++) {
28
+ try {
29
+ return await call();
30
+ }
31
+ catch (error) {
32
+ if (attempt >= BUSY_ATTEMPTS || !refusedBeforeRunning(error))
33
+ throw error;
34
+ await new Promise((resolve) => setTimeout(resolve, 200 * 2 ** (attempt - 1)));
35
+ }
36
+ }
37
+ }
11
38
  /**
12
39
  * The SDK-managed **chunk/voxel cache** — the client-side source of truth
13
40
  * for terrain:
@@ -31,6 +58,9 @@ export class ChunkStore {
31
58
  this.config = config;
32
59
  this.chunks = new Map();
33
60
  this.inFlight = new Set();
61
+ this.hydrating = new Set();
62
+ /** Consecutive failed hydrations per chunk key. */
63
+ this.hydrateFailures = new Map();
34
64
  this.writeBackQueue = [];
35
65
  this.changeListeners = new Set();
36
66
  this.fallbackUuid = generateCrowdyUuid();
@@ -94,56 +124,98 @@ export class ChunkStore {
94
124
  * configured, marks server-unknown chunks `missing`, and seeds them via
95
125
  * `onMissing`. In-flight requests are deduped; safe to call every time the
96
126
  * player crosses a chunk boundary.
127
+ *
128
+ * Requests the platform refuses as busy are asked again with backoff, and
129
+ * at most 8 hydrations run at once. Hydration is best effort: a chunk whose
130
+ * states could not be fetched keeps the voxels the bulk load gave it
131
+ * (`hydrated` stays false) and is hydrated on a later call. A chunk whose
132
+ * bulk load failed is `failed` and is requested again by a later call. The
133
+ * promise rejects only when the bulk load itself fails.
97
134
  */
98
135
  async ensureAround(center, radius) {
99
- const wanted = chunksAround(center, radius).filter((coord) => {
136
+ const around = chunksAround(center, radius);
137
+ const wanted = around.filter((coord) => {
100
138
  const key = chunkKey(coord);
101
- return !this.chunks.has(key) && !this.inFlight.has(key);
139
+ if (this.inFlight.has(key))
140
+ return false;
141
+ const chunk = this.chunks.get(key);
142
+ return !chunk || chunk.loadState === 'failed';
102
143
  });
103
- if (wanted.length === 0)
104
- return;
105
- for (const coord of wanted)
106
- this.inFlight.add(chunkKey(coord));
107
- try {
108
- const response = await this.ctx.client.chunks.byDistance({
109
- appId: this.ctx.appId,
110
- centerCoordinate: toChunkInput(center),
111
- maxDistance: Math.max(1, Math.min(8, radius)),
112
- limit: (2 * radius + 1) ** 3,
113
- });
114
- const returned = new Set();
115
- for (const chunk of response.chunks) {
116
- const coord = fromChunkInput(chunk.coordinates);
117
- returned.add(chunkKey(coord));
118
- this.applyServerChunk(coord, chunk.voxels ?? null, chunk.chunkState ?? null);
144
+ const returned = new Set();
145
+ if (wanted.length > 0) {
146
+ for (const coord of wanted)
147
+ this.inFlight.add(chunkKey(coord));
148
+ try {
149
+ const response = await whenNotBusy(() => this.ctx.client.chunks.byDistance({
150
+ appId: this.ctx.appId,
151
+ centerCoordinate: toChunkInput(center),
152
+ maxDistance: Math.max(1, Math.min(8, radius)),
153
+ limit: (2 * radius + 1) ** 3,
154
+ }));
155
+ for (const chunk of response.chunks) {
156
+ const coord = fromChunkInput(chunk.coordinates);
157
+ returned.add(chunkKey(coord));
158
+ this.applyServerChunk(coord, chunk.voxels ?? null, chunk.chunkState ?? null);
159
+ }
160
+ // Requested-but-absent chunks have never been stored server-side.
161
+ for (const coord of wanted) {
162
+ if (returned.has(chunkKey(coord)))
163
+ continue;
164
+ this.markMissing(coord);
165
+ }
119
166
  }
120
- // Requested-but-absent chunks have never been stored server-side.
121
- for (const coord of wanted) {
122
- if (returned.has(chunkKey(coord)))
123
- continue;
124
- this.markMissing(coord);
167
+ catch (error) {
168
+ for (const coord of wanted) {
169
+ const key = chunkKey(coord);
170
+ const chunk = this.chunks.get(key);
171
+ if (!chunk || chunk.loadState === 'failed') {
172
+ const entry = this.ensureEntry(coord);
173
+ entry.loadState = 'failed';
174
+ this.touch(entry);
175
+ }
176
+ }
177
+ throw error;
125
178
  }
126
- if (this.hydrateStates) {
127
- await Promise.all(wanted
128
- .filter((coord) => returned.has(chunkKey(coord)))
129
- .map((coord) => this.hydrate(coord)));
179
+ finally {
180
+ for (const coord of wanted)
181
+ this.inFlight.delete(chunkKey(coord));
130
182
  }
131
183
  }
132
- catch (error) {
133
- for (const coord of wanted) {
184
+ if (this.hydrateStates) {
185
+ // What the bulk load just returned, and what an earlier call failed to hydrate.
186
+ // Chunks seeded here and written back are the server's copy already.
187
+ await this.hydrateAll(around.filter((coord) => {
188
+ const key = chunkKey(coord);
189
+ const chunk = this.chunks.get(key);
190
+ const failures = this.hydrateFailures.get(key) ?? 0;
191
+ return (chunk?.loadState === 'loaded' &&
192
+ !chunk.hydrated &&
193
+ !this.hydrating.has(key) &&
194
+ (returned.has(key) || (failures > 0 && failures < HYDRATE_GIVE_UP)));
195
+ }));
196
+ }
197
+ }
198
+ /** Hydrates chunks with at most {@link HYDRATE_CONCURRENCY} requests in flight. */
199
+ async hydrateAll(coords) {
200
+ let next = 0;
201
+ const worker = async () => {
202
+ while (next < coords.length) {
203
+ const coord = coords[next++];
134
204
  const key = chunkKey(coord);
135
- if (!this.chunks.has(key)) {
136
- const chunk = this.ensureEntry(coord);
137
- chunk.loadState = 'failed';
138
- this.touch(chunk);
205
+ this.hydrating.add(key);
206
+ try {
207
+ await whenNotBusy(() => this.hydrate(coord));
208
+ this.hydrateFailures.delete(key);
209
+ }
210
+ catch {
211
+ this.hydrateFailures.set(key, (this.hydrateFailures.get(key) ?? 0) + 1);
212
+ }
213
+ finally {
214
+ this.hydrating.delete(key);
139
215
  }
140
216
  }
141
- throw error;
142
- }
143
- finally {
144
- for (const coord of wanted)
145
- this.inFlight.delete(chunkKey(coord));
146
- }
217
+ };
218
+ await Promise.all(Array.from({ length: Math.min(HYDRATE_CONCURRENCY, coords.length) }, worker));
147
219
  }
148
220
  /**
149
221
  * Hydrate one chunk's sparse voxel states (and chunk state) via a
@@ -259,6 +331,7 @@ export class ChunkStore {
259
331
  continue;
260
332
  if (chunkDistance(chunk.coord, center) > radius) {
261
333
  this.chunks.delete(key);
334
+ this.hydrateFailures.delete(key);
262
335
  this.revisionValue += 1;
263
336
  }
264
337
  }
@@ -1 +1 @@
1
- {"version":3,"file":"durable.d.ts","sourceRoot":"","sources":["../../src/stores/durable.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"durable.d.ts","sourceRoot":"","sources":["../../src/stores/durable.ts"],"names":[],"mappings":"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"}
@@ -80,8 +80,9 @@ export class SaveStateStore {
80
80
  const autosave = config.autosaveMs ?? false;
81
81
  if (autosave !== false && autosave > 0) {
82
82
  ctx.onDispose(ctx.ticker.every(autosave, () => {
83
+ // A failed autosave stays dirty, so the next one retries it.
83
84
  if (this.dirtyFlag && !this.saving)
84
- void this.save();
85
+ this.save().catch(() => { });
85
86
  }));
86
87
  }
87
88
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crowdedkingdoms/crowdyjs",
3
- "version": "17.12.0-dev.1",
3
+ "version": "17.14.0-dev.1",
4
4
  "description": "Browser-first Crowded Kingdoms SDK with GraphQL, realtime, and local Rust authoring support",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",