@crowdedkingdoms/crowdyjs 8.3.0 → 8.4.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 (47) hide show
  1. package/README.md +60 -0
  2. package/dist/generated/graphql.d.ts +3 -3
  3. package/dist/generated/graphql.d.ts.map +1 -1
  4. package/dist/index.d.ts +2 -1
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/kit/blueprints/plots.d.ts +10 -1
  8. package/dist/kit/blueprints/plots.d.ts.map +1 -1
  9. package/dist/kit/blueprints/plots.js +11 -6
  10. package/dist/kit/npcs.d.ts.map +1 -1
  11. package/dist/kit/plots.d.ts +5 -1
  12. package/dist/kit/plots.d.ts.map +1 -1
  13. package/dist/kit/social.d.ts.map +1 -1
  14. package/dist/stores/actors.d.ts +316 -0
  15. package/dist/stores/actors.d.ts.map +1 -0
  16. package/dist/stores/actors.js +506 -0
  17. package/dist/stores/chunks.d.ts +180 -0
  18. package/dist/stores/chunks.d.ts.map +1 -0
  19. package/dist/stores/chunks.js +378 -0
  20. package/dist/stores/codec.d.ts +109 -0
  21. package/dist/stores/codec.d.ts.map +1 -0
  22. package/dist/stores/codec.js +186 -0
  23. package/dist/stores/durable.d.ts +144 -0
  24. package/dist/stores/durable.d.ts.map +1 -0
  25. package/dist/stores/durable.js +246 -0
  26. package/dist/stores/errors.d.ts +69 -0
  27. package/dist/stores/errors.d.ts.map +1 -0
  28. package/dist/stores/errors.js +87 -0
  29. package/dist/stores/inbox.d.ts +184 -0
  30. package/dist/stores/inbox.d.ts.map +1 -0
  31. package/dist/stores/inbox.js +326 -0
  32. package/dist/stores/index.d.ts +186 -0
  33. package/dist/stores/index.d.ts.map +1 -0
  34. package/dist/stores/index.js +109 -0
  35. package/dist/stores/keys.d.ts +52 -0
  36. package/dist/stores/keys.d.ts.map +1 -0
  37. package/dist/stores/keys.js +76 -0
  38. package/dist/stores/model.d.ts +81 -0
  39. package/dist/stores/model.d.ts.map +1 -0
  40. package/dist/stores/model.js +163 -0
  41. package/dist/stores/session.d.ts +119 -0
  42. package/dist/stores/session.d.ts.map +1 -0
  43. package/dist/stores/session.js +116 -0
  44. package/dist/stores/ticker.d.ts +44 -0
  45. package/dist/stores/ticker.d.ts.map +1 -0
  46. package/dist/stores/ticker.js +127 -0
  47. package/package.json +6 -1
package/README.md CHANGED
@@ -106,6 +106,7 @@ If `managementUrl` is omitted, the SDK falls back to `httpUrl` for backwards-com
106
106
  | `client.udp` | UDP proxy subscriptions + spatial mutations (`sendActorUpdate`, `sendVoxelUpdate`, `sendAudioPacket`, `sendTextPacket`, `sendClientEvent`). |
107
107
  | `client.realtime` | Connection status, manual `connect()` / `disconnect()`, `onStatus()` listener. |
108
108
  | `client.world(appId)` | Higher-level helpers for browser games (`actor.join`, `actor.sendState`, `actor.sendText`). |
109
+ | `createWorldSession(client, appId, config)` (from `@crowdedkingdoms/crowdyjs/stores`) | World Stores: opt-in, SDK-managed game state — typed codecs (`structCodec` binary DSL), your own actor with a 5 Hz send loop (`session.self`), a remote-actor registry with lanes/history/staleness (`session.actors`), attributed send errors (`session.errors`), a chunk/voxel cache with realtime merge + worldgen write-back (`session.chunks`), channel/direct-message inboxes + a typed event router, host tracking, typed save/avatar state, and a game-model container mirror. Only configured stores exist (compile-time + runtime); unimported stores tree-shake away. |
109
110
  | `client.kit(appId)` | Game Kit: ready-made mappings of game concepts onto the game model — `kit.inventory`, `kit.objects` (lockable doors/chests with custom permissions), `kit.npcs`, `kit.plots` (buy/rent land with transactional, replication-enforced grid grants), and the genre layers `kit.economy` (wallets/shops/trades/market), `kit.progression` (xp/skills/achievements/rating), `kit.loot`, `kit.quests`, `kit.combat`, `kit.matches` (session lobbies/turns/scores with notify-to-pull channels), `kit.decks` (hidden hands), `kit.worldsim` (clock/nodes/crops/waves), `kit.social` (parties/guilds/chat over teams+channels), `kit.leaderboards`, `kit.features` (tier gates) — plus blueprint builders + `kit.deploy(...)` for the admin "load the rules" step. |
110
111
 
111
112
  **Studio-admin surface** (privileged; drive with a server-side / studio token, grouped under `client.admin`):
@@ -269,6 +270,65 @@ await actor.sendToActor(
269
270
 
270
271
  The world helpers are thin wrappers over `client.udp.*` with the appId pre-bound — convenient for browser games. Advanced callers can always use `client.udp.*` with the generated GraphQL input types directly.
271
272
 
273
+ ## World Stores
274
+
275
+ The core client is a thin transport; the **World Stores** layer
276
+ (`@crowdedkingdoms/crowdyjs/stores`, 8.4+) adds the source-of-truth data
277
+ structures every game otherwise hand-writes: actor registries, chunk/voxel
278
+ caches, error attribution, message inboxes, host tracking, and typed
279
+ durable-state wrappers — all driven by ONE shared `udpNotifications`
280
+ subscription and ONE scheduler.
281
+
282
+ ```ts
283
+ import {
284
+ createWorldSession, structCodec, f32, u8, jsonCodec, workerTicker,
285
+ } from '@crowdedkingdoms/crowdyjs/stores';
286
+
287
+ // Describe your replication state ONCE (48-byte binary layouts, declaratively):
288
+ const poseCodec = structCodec({
289
+ x: f32(), y: f32(), z: f32(), yaw: f32(),
290
+ flags: u8(), held: u8(),
291
+ });
292
+
293
+ const session = createWorldSession(game, appId, {
294
+ ticker: workerTicker(), // keep 5 Hz sends even in backgrounded tabs
295
+ self: { codec: poseCodec, initialState: { x: 0, y: 0, z: 0, yaw: 0, flags: 0, held: 0 } },
296
+ actors: { codec: poseCodec, staleAfterMs: 12_000, historySize: 2 },
297
+ errors: true,
298
+ chunks: { voxelStateCodec: jsonCodec<MyVoxelMeta>() },
299
+ });
300
+
301
+ // Your actor: uuid minted + persisted, presence sent at 5 Hz with
302
+ // send-on-change dedup; just update the typed state from your game loop.
303
+ await session.self.join({ x: '0', y: '0', z: '0' });
304
+ session.self.patchState({ x: 12.5, yaw: 1.57 });
305
+ console.log(session.self.status, session.self.lastAck?.state);
306
+
307
+ // Everyone else: typed, self-filtered, staleness-managed — render from it.
308
+ for (const actor of session.actors.list()) {
309
+ render(actor.uuid, actor.state, actor.samples); // samples → interpolation
310
+ }
311
+
312
+ // Terrain: cached, hydrated, realtime-merged, optimistically editable.
313
+ await session.chunks.ensureAround({ x: 0, y: 0, z: 0 }, 3);
314
+ await session.chunks.setVoxel({ chunk: { x: 0, y: 0, z: 0 }, x: 1, y: 2, z: 3, voxelType: 7 });
315
+
316
+ // Server-reported send errors, attributed to what you sent:
317
+ session.errors.onError((e) => console.warn(e.errorCode, e.send?.kind));
318
+
319
+ session.dispose();
320
+ ```
321
+
322
+ Every store is **opt-in twice over**: only configured stores are constructed
323
+ (and only they exist on the session's TYPE — `session.host` without
324
+ `host: ...` in the config is a compile error), and the layer lives behind the
325
+ `./stores` subpath with `"sideEffects": false`, so unimported stores never
326
+ reach your bundle. Reads are synchronous snapshots and writes happen on
327
+ WebSocket events (not `requestAnimationFrame`), so render loops read freely
328
+ and a backgrounded tab keeps ingesting updates; pass `workerTicker()` to also
329
+ keep timer-driven sends at full rate while hidden. See the
330
+ [World Stores guide](https://docs.crowdedkingdoms.com/crowdyjs/stores).
331
+
272
332
  ## Game Kit
273
333
 
274
334
  `client.kit(appId)` maps traditional game concepts onto the abstract game
@@ -133,7 +133,7 @@ export type ActorUpdateRequestInput = {
133
133
  /** A unique identifier for the actor. Must be exactly 32 bytes when encoded as UTF-8. This is typically a client-generated UUID. */
134
134
  uuid: Scalars['String']['input'];
135
135
  };
136
- /** Response from the UDP game server for an actor update request. Received via the udpNotifications subscription. */
136
+ /** LEGACY never emitted. The game server retired the dedicated actor-update response opcode (129); an applied update now arrives as your own ActorUpdateNotification (the sender is included in the chunk fan-out) and failures arrive as GenericErrorResponse. This type remains in the UdpNotification union for backward compatibility only — do not select it in new code; it will be removed in a future major version. */
137
137
  export type ActorUpdateResponse = {
138
138
  __typename?: 'ActorUpdateResponse';
139
139
  /** The ID of the app where the actor update was processed. */
@@ -5782,7 +5782,7 @@ export declare enum UdpErrorCode {
5782
5782
  /** This client has no authenticated session on the server. Complete the UDP token handshake (or open the UDP proxy) before sending spatial messages. */
5783
5783
  UserNotAuthenticated = "USER_NOT_AUTHENTICATED"
5784
5784
  }
5785
- /** All game-server messages delivered over the UDP proxy as GraphQL payloads. Subscribe to udpNotifications before or with sending mutations so responses and GenericErrorResponse (correlate via sequenceNumber) are not missed. */
5785
+ /** All game-server messages delivered over the UDP proxy as GraphQL payloads. Subscribe to udpNotifications before or with sending mutations so responses and GenericErrorResponse (correlate via sequenceNumber) are not missed. NOTE: the ActorUpdateResponse and VoxelUpdateResponse members are LEGACY and never emitted (applied updates arrive as your own *Notification self-echo; failures as GenericErrorResponse) — they remain in the union for backward compatibility and will be removed in a future major version. */
5786
5786
  export type UdpNotification = ActorUpdateNotification | ActorUpdateResponse | ChannelMessageNotification | ClientAudioNotification | ClientEventNotification | ClientTextNotification | GenericErrorResponse | RealtimeConnectionEvent | ServerEventNotification | SingleActorMessageNotification | VoxelUpdateNotification | VoxelUpdateResponse;
5787
5787
  /** UDP proxy session for the game token on the request. Returned by udpProxyConnectionStatus and connectUdpProxy. Binary UDP layouts are documented in database/client-wire-formats.md. */
5788
5788
  export type UdpProxyConnectionStatus = {
@@ -6417,7 +6417,7 @@ export type VoxelUpdateRequestInput = {
6417
6417
  /** The new voxel type ID. This determines the appearance and properties of the voxel. */
6418
6418
  voxelType: Scalars['Int']['input'];
6419
6419
  };
6420
- /** Response from the UDP game server for a voxel update request. Received via the udpNotifications subscription. */
6420
+ /** LEGACY never emitted. The game server retired the dedicated voxel-update response opcode (132); an applied update now arrives as your own VoxelUpdateNotification (the sender is included in the chunk fan-out) and failures arrive as GenericErrorResponse. This type remains in the UdpNotification union for backward compatibility only — do not select it in new code; it will be removed in a future major version. */
6421
6421
  export type VoxelUpdateResponse = {
6422
6422
  __typename?: 'VoxelUpdateResponse';
6423
6423
  /** The ID of the app where the voxel update was processed. */