@crowdedkingdoms/crowdyjs 17.13.0-dev.1 → 18.0.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 (284) hide show
  1. package/MIGRATION.md +163 -1
  2. package/README.md +142 -146
  3. package/dist/crowdy-client.d.ts +12 -31
  4. package/dist/crowdy-client.d.ts.map +1 -1
  5. package/dist/crowdy-client.js +15 -37
  6. package/dist/crowdy-dsh/bridge.d.ts.map +1 -1
  7. package/dist/crowdy-dsh/bridge.js +3 -6
  8. package/dist/crowdy-studio/controller.d.ts +59 -57
  9. package/dist/crowdy-studio/controller.d.ts.map +1 -1
  10. package/dist/crowdy-studio/controller.js +268 -206
  11. package/dist/crowdy-studio/diagnostics.d.ts +3 -12
  12. package/dist/crowdy-studio/diagnostics.d.ts.map +1 -1
  13. package/dist/crowdy-studio/dom-shell.d.ts +2 -4
  14. package/dist/crowdy-studio/dom-shell.d.ts.map +1 -1
  15. package/dist/crowdy-studio/dom-shell.js +16 -47
  16. package/dist/crowdy-studio/embed/hud-layer.d.ts +1 -1
  17. package/dist/crowdy-studio/embed/panel.d.ts +3 -12
  18. package/dist/crowdy-studio/embed/panel.d.ts.map +1 -1
  19. package/dist/crowdy-studio/embed/panel.js +3 -6
  20. package/dist/crowdy-studio/index.d.ts +1 -3
  21. package/dist/crowdy-studio/index.d.ts.map +1 -1
  22. package/dist/crowdy-studio/index.js +0 -2
  23. package/dist/crowdy-studio/models.d.ts +3 -4
  24. package/dist/crowdy-studio/models.d.ts.map +1 -1
  25. package/dist/crowdy-studio/starter-projects.d.ts +8 -4
  26. package/dist/crowdy-studio/starter-projects.d.ts.map +1 -1
  27. package/dist/crowdy-studio/starter-projects.js +83 -100
  28. package/dist/domains/appAccess.d.ts +14 -1
  29. package/dist/domains/appAccess.d.ts.map +1 -1
  30. package/dist/domains/appAccess.js +37 -1
  31. package/dist/domains/exec.d.ts +162 -15
  32. package/dist/domains/exec.d.ts.map +1 -1
  33. package/dist/domains/exec.js +180 -18
  34. package/dist/domains/host.d.ts +3 -3
  35. package/dist/domains/host.js +3 -3
  36. package/dist/domains/marketplace.d.ts +9 -58
  37. package/dist/domains/marketplace.d.ts.map +1 -1
  38. package/dist/domains/marketplace.js +9 -106
  39. package/dist/domains/playerWallet.d.ts +6 -12
  40. package/dist/domains/playerWallet.d.ts.map +1 -1
  41. package/dist/domains/playerWallet.js +6 -21
  42. package/dist/errors.d.ts +7 -13
  43. package/dist/errors.d.ts.map +1 -1
  44. package/dist/errors.js +6 -10
  45. package/dist/generated/graphql.d.ts +1384 -7977
  46. package/dist/generated/graphql.d.ts.map +1 -1
  47. package/dist/generated/graphql.js +22 -321
  48. package/dist/grid-mods/exec-client-halves.d.ts +156 -0
  49. package/dist/grid-mods/exec-client-halves.d.ts.map +1 -0
  50. package/dist/grid-mods/exec-client-halves.js +356 -0
  51. package/dist/grid-mods/grid-host-calls.d.ts +29 -15
  52. package/dist/grid-mods/grid-host-calls.d.ts.map +1 -1
  53. package/dist/grid-mods/grid-host-calls.js +40 -44
  54. package/dist/grid-mods/index.d.ts +1 -0
  55. package/dist/grid-mods/index.d.ts.map +1 -1
  56. package/dist/grid-mods/index.js +1 -0
  57. package/dist/grid-mods/mod-runtime.d.ts +21 -6
  58. package/dist/grid-mods/mod-runtime.d.ts.map +1 -1
  59. package/dist/grid-mods/mod-runtime.js +6 -2
  60. package/dist/grid-scope.d.ts +0 -188
  61. package/dist/grid-scope.d.ts.map +1 -1
  62. package/dist/grid-scope.js +0 -69
  63. package/dist/index.d.ts +12 -18
  64. package/dist/index.d.ts.map +1 -1
  65. package/dist/index.js +11 -16
  66. package/dist/kit/actions.d.ts +5 -9
  67. package/dist/kit/actions.d.ts.map +1 -1
  68. package/dist/kit/actions.js +5 -9
  69. package/dist/kit/index.d.ts +2 -33
  70. package/dist/kit/index.d.ts.map +1 -1
  71. package/dist/kit/index.js +1 -32
  72. package/dist/kit/kit.d.ts +8 -208
  73. package/dist/kit/kit.d.ts.map +1 -1
  74. package/dist/kit/kit.js +5 -163
  75. package/dist/kit/social.d.ts +1 -3
  76. package/dist/kit/social.d.ts.map +1 -1
  77. package/dist/kit/social.js +1 -3
  78. package/dist/kit/wire.d.ts +16 -5
  79. package/dist/kit/wire.d.ts.map +1 -1
  80. package/dist/kit/wire.js +19 -5
  81. package/dist/live-coding/assets/browser-authoring-index.json +1 -1
  82. package/dist/live-coding/assets/manifest.json +1 -1
  83. package/dist/live-coding/browser-authoring-index.generated.d.ts.map +1 -1
  84. package/dist/live-coding/browser-authoring-index.generated.js +1496 -4386
  85. package/dist/live-coding/rust-analysis.d.ts.map +1 -1
  86. package/dist/live-coding/rust-analysis.js +31 -23
  87. package/dist/player-runtime/client-host-calls.d.ts +8 -0
  88. package/dist/player-runtime/client-host-calls.d.ts.map +1 -0
  89. package/dist/player-runtime/client-host-calls.js +28 -0
  90. package/dist/player-runtime/glue-runtime.d.ts +27 -10
  91. package/dist/player-runtime/glue-runtime.d.ts.map +1 -1
  92. package/dist/player-runtime/glue-runtime.js +40 -45
  93. package/dist/player-runtime/glue-sab.d.ts +2 -2
  94. package/dist/player-runtime/glue-sab.js +2 -2
  95. package/dist/player-runtime/player-code-broker.d.ts +80 -24
  96. package/dist/player-runtime/player-code-broker.d.ts.map +1 -1
  97. package/dist/player-runtime/player-code-broker.js +169 -41
  98. package/dist/player-runtime/player-glue-worker.d.ts +8 -8
  99. package/dist/player-runtime/player-glue-worker.d.ts.map +1 -1
  100. package/dist/player-runtime/player-glue-worker.js +26 -9
  101. package/dist/stores/actors.d.ts +12 -2
  102. package/dist/stores/actors.d.ts.map +1 -1
  103. package/dist/stores/actors.js +43 -18
  104. package/dist/stores/chunks.d.ts +12 -0
  105. package/dist/stores/chunks.d.ts.map +1 -1
  106. package/dist/stores/chunks.js +112 -39
  107. package/dist/stores/durable.d.ts.map +1 -1
  108. package/dist/stores/durable.js +2 -1
  109. package/dist/stores/inbox.d.ts +1 -1
  110. package/dist/stores/inbox.d.ts.map +1 -1
  111. package/dist/stores/index.d.ts +0 -11
  112. package/dist/stores/index.d.ts.map +1 -1
  113. package/dist/stores/index.js +0 -4
  114. package/dist/stores/session.d.ts +0 -2
  115. package/dist/stores/session.d.ts.map +1 -1
  116. package/package.json +1 -1
  117. package/dist/crowdy-studio/model-lint-log.d.ts +0 -76
  118. package/dist/crowdy-studio/model-lint-log.d.ts.map +0 -1
  119. package/dist/crowdy-studio/model-lint-log.js +0 -93
  120. package/dist/crowdy-studio/model-lint.d.ts +0 -61
  121. package/dist/crowdy-studio/model-lint.d.ts.map +0 -1
  122. package/dist/crowdy-studio/model-lint.js +0 -45
  123. package/dist/domains/compute.d.ts +0 -208
  124. package/dist/domains/compute.d.ts.map +0 -1
  125. package/dist/domains/compute.js +0 -274
  126. package/dist/domains/controlPlane.d.ts +0 -38
  127. package/dist/domains/controlPlane.d.ts.map +0 -1
  128. package/dist/domains/controlPlane.js +0 -43
  129. package/dist/domains/gameModel.d.ts +0 -1174
  130. package/dist/domains/gameModel.d.ts.map +0 -1
  131. package/dist/domains/gameModel.js +0 -1333
  132. package/dist/domains/playerCompute.d.ts +0 -91
  133. package/dist/domains/playerCompute.d.ts.map +0 -1
  134. package/dist/domains/playerCompute.js +0 -136
  135. package/dist/domains/playerModel.d.ts +0 -20
  136. package/dist/domains/playerModel.d.ts.map +0 -1
  137. package/dist/domains/playerModel.js +0 -54
  138. package/dist/kit/abilities.d.ts +0 -66
  139. package/dist/kit/abilities.d.ts.map +0 -1
  140. package/dist/kit/abilities.js +0 -71
  141. package/dist/kit/blueprints/combat.d.ts +0 -81
  142. package/dist/kit/blueprints/combat.d.ts.map +0 -1
  143. package/dist/kit/blueprints/combat.js +0 -357
  144. package/dist/kit/blueprints/core.d.ts +0 -222
  145. package/dist/kit/blueprints/core.d.ts.map +0 -1
  146. package/dist/kit/blueprints/core.js +0 -185
  147. package/dist/kit/blueprints/decks.d.ts +0 -50
  148. package/dist/kit/blueprints/decks.d.ts.map +0 -1
  149. package/dist/kit/blueprints/decks.js +0 -163
  150. package/dist/kit/blueprints/economy.d.ts +0 -88
  151. package/dist/kit/blueprints/economy.d.ts.map +0 -1
  152. package/dist/kit/blueprints/economy.js +0 -533
  153. package/dist/kit/blueprints/guild.d.ts +0 -51
  154. package/dist/kit/blueprints/guild.d.ts.map +0 -1
  155. package/dist/kit/blueprints/guild.js +0 -50
  156. package/dist/kit/blueprints/index.d.ts +0 -23
  157. package/dist/kit/blueprints/index.d.ts.map +0 -1
  158. package/dist/kit/blueprints/index.js +0 -22
  159. package/dist/kit/blueprints/inventory.d.ts +0 -84
  160. package/dist/kit/blueprints/inventory.d.ts.map +0 -1
  161. package/dist/kit/blueprints/inventory.js +0 -257
  162. package/dist/kit/blueprints/leaderboards.d.ts +0 -49
  163. package/dist/kit/blueprints/leaderboards.d.ts.map +0 -1
  164. package/dist/kit/blueprints/leaderboards.js +0 -126
  165. package/dist/kit/blueprints/liveops.d.ts +0 -42
  166. package/dist/kit/blueprints/liveops.d.ts.map +0 -1
  167. package/dist/kit/blueprints/liveops.js +0 -170
  168. package/dist/kit/blueprints/locks.d.ts +0 -82
  169. package/dist/kit/blueprints/locks.d.ts.map +0 -1
  170. package/dist/kit/blueprints/locks.js +0 -155
  171. package/dist/kit/blueprints/loot.d.ts +0 -90
  172. package/dist/kit/blueprints/loot.d.ts.map +0 -1
  173. package/dist/kit/blueprints/loot.js +0 -229
  174. package/dist/kit/blueprints/matches.d.ts +0 -84
  175. package/dist/kit/blueprints/matches.d.ts.map +0 -1
  176. package/dist/kit/blueprints/matches.js +0 -349
  177. package/dist/kit/blueprints/moderation.d.ts +0 -32
  178. package/dist/kit/blueprints/moderation.d.ts.map +0 -1
  179. package/dist/kit/blueprints/moderation.js +0 -140
  180. package/dist/kit/blueprints/npcs.d.ts +0 -78
  181. package/dist/kit/blueprints/npcs.d.ts.map +0 -1
  182. package/dist/kit/blueprints/npcs.js +0 -125
  183. package/dist/kit/blueprints/plots.d.ts +0 -63
  184. package/dist/kit/blueprints/plots.d.ts.map +0 -1
  185. package/dist/kit/blueprints/plots.js +0 -151
  186. package/dist/kit/blueprints/progression.d.ts +0 -68
  187. package/dist/kit/blueprints/progression.d.ts.map +0 -1
  188. package/dist/kit/blueprints/progression.js +0 -324
  189. package/dist/kit/blueprints/quests.d.ts +0 -72
  190. package/dist/kit/blueprints/quests.d.ts.map +0 -1
  191. package/dist/kit/blueprints/quests.js +0 -285
  192. package/dist/kit/blueprints/worldsim.d.ts +0 -91
  193. package/dist/kit/blueprints/worldsim.d.ts.map +0 -1
  194. package/dist/kit/blueprints/worldsim.js +0 -468
  195. package/dist/kit/combat.d.ts +0 -141
  196. package/dist/kit/combat.d.ts.map +0 -1
  197. package/dist/kit/combat.js +0 -196
  198. package/dist/kit/decks.d.ts +0 -157
  199. package/dist/kit/decks.d.ts.map +0 -1
  200. package/dist/kit/decks.js +0 -186
  201. package/dist/kit/director.d.ts +0 -88
  202. package/dist/kit/director.d.ts.map +0 -1
  203. package/dist/kit/director.js +0 -86
  204. package/dist/kit/economy.d.ts +0 -289
  205. package/dist/kit/economy.d.ts.map +0 -1
  206. package/dist/kit/economy.js +0 -427
  207. package/dist/kit/engine.d.ts +0 -48
  208. package/dist/kit/engine.d.ts.map +0 -1
  209. package/dist/kit/engine.js +0 -113
  210. package/dist/kit/features.d.ts +0 -60
  211. package/dist/kit/features.d.ts.map +0 -1
  212. package/dist/kit/features.js +0 -65
  213. package/dist/kit/instances.d.ts +0 -52
  214. package/dist/kit/instances.d.ts.map +0 -1
  215. package/dist/kit/instances.js +0 -60
  216. package/dist/kit/inventory.d.ts +0 -129
  217. package/dist/kit/inventory.d.ts.map +0 -1
  218. package/dist/kit/inventory.js +0 -200
  219. package/dist/kit/leaderboards.d.ts +0 -86
  220. package/dist/kit/leaderboards.d.ts.map +0 -1
  221. package/dist/kit/leaderboards.js +0 -133
  222. package/dist/kit/liveops.d.ts +0 -112
  223. package/dist/kit/liveops.d.ts.map +0 -1
  224. package/dist/kit/liveops.js +0 -183
  225. package/dist/kit/loot.d.ts +0 -128
  226. package/dist/kit/loot.d.ts.map +0 -1
  227. package/dist/kit/loot.js +0 -160
  228. package/dist/kit/matches.d.ts +0 -301
  229. package/dist/kit/matches.d.ts.map +0 -1
  230. package/dist/kit/matches.js +0 -458
  231. package/dist/kit/matchmaking.d.ts +0 -57
  232. package/dist/kit/matchmaking.d.ts.map +0 -1
  233. package/dist/kit/matchmaking.js +0 -59
  234. package/dist/kit/minigames.d.ts +0 -30
  235. package/dist/kit/minigames.d.ts.map +0 -1
  236. package/dist/kit/minigames.js +0 -38
  237. package/dist/kit/mobs.d.ts +0 -90
  238. package/dist/kit/mobs.d.ts.map +0 -1
  239. package/dist/kit/mobs.js +0 -98
  240. package/dist/kit/moderation.d.ts +0 -73
  241. package/dist/kit/moderation.d.ts.map +0 -1
  242. package/dist/kit/moderation.js +0 -103
  243. package/dist/kit/movement.d.ts +0 -78
  244. package/dist/kit/movement.d.ts.map +0 -1
  245. package/dist/kit/movement.js +0 -103
  246. package/dist/kit/notifications.d.ts +0 -45
  247. package/dist/kit/notifications.d.ts.map +0 -1
  248. package/dist/kit/notifications.js +0 -67
  249. package/dist/kit/npcs.d.ts +0 -226
  250. package/dist/kit/npcs.d.ts.map +0 -1
  251. package/dist/kit/npcs.js +0 -141
  252. package/dist/kit/objects.d.ts +0 -116
  253. package/dist/kit/objects.d.ts.map +0 -1
  254. package/dist/kit/objects.js +0 -119
  255. package/dist/kit/pets.d.ts +0 -85
  256. package/dist/kit/pets.d.ts.map +0 -1
  257. package/dist/kit/pets.js +0 -96
  258. package/dist/kit/plots.d.ts +0 -85
  259. package/dist/kit/plots.d.ts.map +0 -1
  260. package/dist/kit/plots.js +0 -113
  261. package/dist/kit/progression.d.ts +0 -181
  262. package/dist/kit/progression.d.ts.map +0 -1
  263. package/dist/kit/progression.js +0 -265
  264. package/dist/kit/quests.d.ts +0 -171
  265. package/dist/kit/quests.d.ts.map +0 -1
  266. package/dist/kit/quests.js +0 -237
  267. package/dist/kit/racing.d.ts +0 -85
  268. package/dist/kit/racing.d.ts.map +0 -1
  269. package/dist/kit/racing.js +0 -99
  270. package/dist/kit/shared.d.ts +0 -73
  271. package/dist/kit/shared.d.ts.map +0 -1
  272. package/dist/kit/shared.js +0 -101
  273. package/dist/kit/telemetry.d.ts +0 -66
  274. package/dist/kit/telemetry.d.ts.map +0 -1
  275. package/dist/kit/telemetry.js +0 -133
  276. package/dist/kit/territory.d.ts +0 -97
  277. package/dist/kit/territory.d.ts.map +0 -1
  278. package/dist/kit/territory.js +0 -114
  279. package/dist/kit/worldsim.d.ts +0 -263
  280. package/dist/kit/worldsim.d.ts.map +0 -1
  281. package/dist/kit/worldsim.js +0 -268
  282. package/dist/stores/model.d.ts +0 -81
  283. package/dist/stores/model.d.ts.map +0 -1
  284. package/dist/stores/model.js +0 -163
package/MIGRATION.md CHANGED
@@ -1,3 +1,164 @@
1
+ # CrowdyJS v18 — the legacy engines are gone
2
+
3
+ **Breaking.** The game API deleted its legacy developer-code engines on dev (cks-game-api #417,
4
+ ck-api `v2.27.0`): Studio compute, the game model and its automations, player compute (its
5
+ browser CLIENT modules too) and the player model. `18.0.0` removes their SDK surface. Their
6
+ code moves to ck-exec (`client.exec`): an app's server code as hubs and spokes, a player's grid
7
+ code as mods, and a mod's browser code as its CLIENT half. Nothing is migrated for you; port the
8
+ code, then upgrade.
9
+
10
+ **What it needs.** 18.0.0 calls nothing the legacy engines served, so it runs against any API
11
+ with ck-exec CLIENT halves (ck-api `v2.24.0`, as 17.14.0 did). **Crowdy Studio's CLIENT
12
+ projects need ck-api `v2.25.1` or later:** `v2.24.0` and `v2.25.0` refuse to save a
13
+ `crowdy-client-sdk` crate in a Studio project (`CROWDY_STUDIO_MANIFEST_INVALID`, cks-game-api
14
+ #425). The other way round, a 17.x SDK against `v2.27.0` fails every call into a deleted field
15
+ as a GraphQL validation error.
16
+
17
+ | 17.x | 18.0 |
18
+ | --- | --- |
19
+ | `client.gameModel` (containers, properties, functions, `invoke`, sessions, events, timers, automations, `seed`, lint, the player-count feed) | A hub's own state and endpoints: `client.exec.connect(appId, { nodeType, key })`, then `call` / `subscribe`; timers are `ctx.timer_after` / `ctx.timer_every` in the hub; sessions are a keyed hub (the `session` starter) |
20
+ | `gameModel.defineFeature`, `grantTierFeature`, `revokeTierFeature`, `features`, `tierFeatures` | `client.appAccess.defineFeature`, `grantTierFeature`, `revokeTierFeature`, `features(appId)`, `tierFeatures(appId, tierId?)` (same fields; a hub reads them with `players.features`) |
21
+ | `client.compute` (modules, `invoke`, templates, runs, logs) | `client.exec.starters` / `build` / `waitForBuild` / `deploy`, `logs`, `instances`, `versions`, `activateVersion`, `setEnabled`; manual calls through `connectAsDeveloper` |
22
+ | `client.playerModel` (containers, automations) | A mod's own state and timers |
23
+ | `client.playerCompute` SERVER side (`deploy` with `target: SERVER`, `setEnabled`, `setRequires`, `invoke`, `runs`, `logs`) | Mods: `client.exec.modBuild` / `waitForModBuild` / `modDeploy` / `modSetEnabled`, calls through `connect(appId, { nodeType: execModType(name), key: gridId })`, `modLogs` |
24
+ | `client.playerCompute` CLIENT modules (`deploy`, `versions`, `artifact`, `artifactBytes`, `usage`, `myModules`, `delete`, `setSwitch`, `switches`) | A mod's CLIENT half: `client.exec.modClientBuild` (polled with `modBuildStatus` / `waitForModBuild`), `modClientDeploy` / `modClientDelete`, `modClientArtifactBytes`; its kill ladder is the mod's, `modSetSwitch` |
25
+ | `marketplace.gridClientMods`, `consentGridClientMod`, `trustGridAuthor`, `clientArtifact`, `clientArtifactBytes`, and `client.grid(...).compute.clientMods` | `client.exec.gridClientMods`, `consentClientMod`, `trustAuthor`, `modClientArtifactBytes` (which checks the bytes against their digest); `ExecClientHalves` runs a grid's CLIENT halves |
26
+ | `marketplace.listings` / `versions` / `publishListing` / `publishVersion` / `acquire` / `install` / `uninstall` / `myAcquisitions` / `myInstalls` | `client.exec.modPublish` / `modListings` / `modInstall` / `modUnpublish`; a listing carries its mod's CLIENT half |
27
+ | `playerWallet.policies` / `setPolicy` / `deletePolicy` (`playerWasmPolicies`) | Mods run under the platform's mod limits; the kill ladder is `client.exec.modSetSwitch` |
28
+ | `client.operator` (`computePlatformCeilings`, `setComputePlatformCeilings`) | ck-exec code is bounded by its manifest limits within the platform's; there is nothing to call |
29
+ | `client.kit(appId)` blueprints, `kit.deploy`, engines, `kit.inventory` … `kit.minigames`, `kit.features` | Hubs; the starters for NPCs and mobs, sessions and matchmaking. `kit.social` stays; `kit/wire.ts` codecs and `runOptimisticAction` stay |
30
+ | `createWorldSession({ model })` (`ContainerMirror`) | Subscribe to the hub's topic |
31
+ | `client.grid(...).sessions`, `.model`, `.compute` | A mod on the grid (`client.exec.mod*`) |
32
+ | `PlayerCodeBroker` / `startGridMod` without `engine` (a legacy CLIENT module: unmetered, with the whole client catalog), `ALLOWED_HOST_CALLS` | A CLIENT half, the only kind of player module: `artifactHash`, `fuelPerDispatch` and `consentedHostCalls` are required, the allowlist is `EXEC_CLIENT_HOST_CALLS`, and the module must carry the `ck_fuel` meter |
33
+ | `MODEL_LINT_QUERY`, `modelLintDiagnostics`, `CrowdyModelLintLog`, the `'model-lint'` diagnostic source | The Rust compiler's diagnostics from the build |
34
+
35
+ - **The player runtime runs CLIENT halves only.** `PlayerCodeBroker`'s `artifactHash` (the
36
+ served `digest`), `fuelPerDispatch` and `consentedHostCalls` are required, and `engine` is
37
+ `'ck-exec'` or absent; a broker without them, or asked for another engine, does not start.
38
+ The glue offers exactly `EXEC_CLIENT_ABI_IMPORTS` and refuses a module without the `ck_fuel`
39
+ meter; its inert wasi stubs and the `wasi_unstable` alias are gone. `ALLOWED_HOST_CALLS` is
40
+ gone, and `GLUE_HOST_FUNCTIONS` names the CLIENT allowlist. `startGridMod`'s wasm spec needs
41
+ the same three values.
42
+ - **CLIENT host calls the legacy engines answered are refused** by the broker as not allowed:
43
+ the Game Model group (`container_*`, `containers_list`, `property_set`, `edge_*`,
44
+ `model_invoke`), `sessions_list`, and `grid_state_get` / `grid_state_set`. A mod's server
45
+ half, a hub keyed by the grid, holds grid state now. `createGridHostCalls` / `startGridMod`
46
+ lost `allowModelInvoke` and the client's `gameModel`.
47
+ - **What a CLIENT half says and answers, on the page** (new):
48
+ - `onLog`: its `crowdy::log` lines (crowdy-client-sdk levels 0 debug, 1 info, 2 warn,
49
+ 3 error), at most `PLAYER_CODE_LOG_LINES_PER_SECOND` (20) a second and
50
+ `PLAYER_CODE_LOG_MAX_CHARS` (1,000) characters each, then a `warn` counting what was
51
+ dropped. On `PlayerCodeBroker`, `ExecClientHalves` (with the mod) and `startGridMod`. The
52
+ text is the mod author's: render it as text. Crowdy Studio shows its preview's lines in
53
+ Logs.
54
+ - `invoke(payload)`: the module's `handle_invoke` export, called by the page:
55
+ `PlayerCodeBroker.invoke`, `ExecClientHalves.invoke(modId, payload)` and
56
+ `RunningGridMod.invoke`, up to `PLAYER_CODE_INVOKE_MAX_BYTES` (256 KiB) each way. The reply
57
+ is the module's: treat it as untrusted input.
58
+ - `createGridHostCalls` answers `avatar_state_get` and `grid_permission_check` as the host
59
+ catalog scopes them: an avatar whose live actor the game places inside the grid
60
+ (`local.avatarChunk`, then the public `avatarAppState`), and the visiting player
61
+ (`userId`) on this grid, from the permission keys the game knows
62
+ (`local.gridPermissionKeys`). Without those they are refused as not offered, as
63
+ `actors_list` is without `local.actorsInChunk`.
64
+ - **Crowdy Studio runs on ck-exec only.** Drop `serverEngine` from the embed, the controller
65
+ and `mountCrowdyStudio`, and `playerCompute` from the embed's services and the controller's
66
+ and `mountCrowdyStudio`'s options (`CrowdyStudioPlayerCompute` is gone); they require
67
+ `mods: client.exec`, and the embed requires `client.exec`. `CrowdyStudioState` loses
68
+ `serverEngine`, `runs` (the Runs panel went) and `usage` (`CrowdyStudioUsageSnapshot`:
69
+ nothing on ck-exec spends player compute quota, and the `'usage'` surface reads the
70
+ wallet); `logs` are `CrowdyStudioLogLine`s (`id`, `source` `'mod' | 'preview'`,
71
+ `moduleName`, `level`, `at`, `text`); `CrowdyStudioInvokeResult` is
72
+ `{ resultJson, durationUs }`. `createCrowdyStudioStarterProject` needs `modStarter` for a
73
+ kind with a SERVER target and has no `engine`: a CLIENT target is a `crowdy-client-sdk`
74
+ CLIENT half. The pairing control went: a mod has no pairing.
75
+ - A CLIENT crate still on `crowdy-compute-sdk` is refused before any build, with what to
76
+ change: `crowdy-client-sdk = "0.1.0"` in `Cargo.toml` and `crowdy_client_sdk` in place of
77
+ `crowdy_compute_sdk` in `src/`. Its host calls are the same less the Game Model, sessions and
78
+ grid state (`grid_state_get` / `grid_state_set`).
79
+ - `ENGINE_SWITCHED_OFF` left `CrowdyFaultCode`: nothing raises it now.
80
+ - The kit's type-98 parser is `parseZoneChangeEvent` beside the other wire parsers.
81
+
82
+ `schema.gql` and the generated types follow the game API: the release carries cks-game-api
83
+ `dev`'s SDL after #417 merged (`npm run schema:sync:paths -- --schema <that schema.gql>`, then
84
+ `npm run codegen`).
85
+
86
+ # 17.14.0 ck-exec CLIENT halves
87
+
88
+ Dev-tier preview (cks-game-api #422, P3 W1). **The release needs a game API that has #422:**
89
+ the build and listing documents now select `ExecBuild.kind`, the artifacts' capability fields
90
+ and the listings' `client*` fields, which an older API refuses as a validation error. Deploy
91
+ the API first.
92
+
93
+ A mod can carry a **CLIENT half**: browser WASM from one `crowdy-client-sdk` crate, built on
94
+ the platform, attached to the mod and served by its grid to visitors who consent to it or
95
+ trust its author. It replaces the legacy grid-attached client mods, which keep working until
96
+ 18.0.
97
+
98
+ - **`client.exec`**: `modClientBuild(appId, crate)` (a build of `kind` `client`; poll it with
99
+ `modBuildStatus` / `waitForModBuild`), `modClientDeploy(appId, gridId, name, buildId)` →
100
+ `ExecModClient`, `modClientDelete(appId, gridId, name)`, `gridClientMods(appId, gridId)` →
101
+ `ExecGridClientMod[]` (with `capabilitySummary` and `authorCapabilitySummary` parsed beside
102
+ the JSON), `consentClientMod(appId, modId, capabilityHash)`, `trustAuthor(appId, gridId,
103
+ authorId, capabilityHash)`, `modClientArtifact(appId, modId)` and
104
+ `modClientArtifactBytes(appId, modId)`, which decodes the module, recomputes its SHA-256 and
105
+ refuses bytes that differ from `digest` (or a CLIENT ABI other than
106
+ `EXEC_CLIENT_ABI_VERSION`, 0, or a capability summary that does not parse) with a
107
+ `CrowdyProtocolError`. Errors are the API's: a stale
108
+ hash is `CONFLICT`, every artifact refusal `NOT_FOUND`, more than 12 fetches a minute per
109
+ player and mod `RATE_LIMITED`.
110
+ - **Types**: `ExecBuild.kind`; `ExecBuildArtifact` (`capabilitySummaryJson`,
111
+ `capabilitySummary`, `capabilityHash`, `tickIntervalMs`, null for a ck-exec module);
112
+ `ExecModListing.clientDigest`, `clientCapabilitySummaryJson`, `clientCapabilitySummary`,
113
+ `clientCapabilityHash`, `clientTickIntervalMs`; `ExecClientCapabilitySummary`,
114
+ `ExecModClient`, `ExecGridClientMod`, `ExecModClientArtifact`, `ExecModClientArtifactBytes`.
115
+ - **`ExecClientHalves`**, the exec twin of the-construct's `runConsentedGridMod` and
116
+ `ClientModLifecycle`: tell it the grid (`enterGrid`) and `refresh()` on a cadence. It lists
117
+ the grid's CLIENT halves, stops the ones removed or changed (keyed by `modId`, `digest`,
118
+ `capabilityHash` and tick interval), asks the player once per author (`trustAuthor`) or per
119
+ CLIENT half (`ask: 'mod'`, `consentClientMod`) through your `confirm`, fetches and caches by
120
+ digest, and runs each in a `PlayerCodeBroker` with its fuel budget and tick interval.
121
+ `NOT_FOUND` holds a CLIENT half back 15 s, `RATE_LIMITED` 60 s, refused bytes and a tripped
122
+ circuit 60 s.
123
+ - **`PlayerCodeBroker({ engine: 'ck-exec' })`**: the allowlist is `EXEC_CLIENT_HOST_CALLS`,
124
+ exactly what crowdy-client-sdk calls (the client catalog less the Game Model group,
125
+ `sessions_list` and `grid_state_*`); the glue offers exactly `EXEC_CLIENT_ABI_IMPORTS`
126
+ (`ck::{log,now_ms,state_get,state_set,host_call}`, `wasi_snapshot_preview1::random_get`),
127
+ refuses a module without the `ck_fuel` meter, and the broker will not start without
128
+ `artifactHash`, `fuelPerDispatch` and `consentedHostCalls` (the served capability summary's
129
+ `hostFunctions`). A call outside that summary is refused too: the build derives the summary by
130
+ scanning the module for host-call names, so a name assembled at run time would otherwise reach
131
+ calls the player never consented to. The default, `'player-compute'`, is unchanged for legacy
132
+ CLIENT modules. `startGridMod`'s wasm spec takes `engine` and `consentedHostCalls` too.
133
+ - **Crowdy Studio's CLIENT target runs on ck-exec** with `serverEngine: 'ck-exec'` (the
134
+ default when `exec` is present). A new CLIENT target starts from a `crowdy-client-sdk` crate
135
+ (`createCrowdyStudioStarterProject({ engine: 'ck-exec' })`); Test draft and Deploy live
136
+ build it with `modClientBuild`, attach it to the project's mod with `modClientDeploy`,
137
+ consent to it as its author and preview the served module with `engine: 'ck-exec'`. A
138
+ CLIENT-only project's CLIENT half rides the mod named for its CLIENT module (which must be a
139
+ mod name now): with no such mod of the player's on the grid, Studio deploys the mod starter's
140
+ server half under it first and says so in the build log, switches it on, and Stop switches
141
+ it off. The preview loads only for a player with `run_client_code` standing in the grid. On
142
+ ck-exec no target reads player compute usage, and the pairing select is disabled.
143
+ - **`CrowdyStudioMods` gained `myMods`, `modClientBuild`, `modClientDeploy`,
144
+ `consentClientMod` and `modClientArtifactBytes`.** Pass `client.exec`; a hand-written
145
+ stand-in needs those too.
146
+ - **Superseded** (`@deprecated`, removed in 18.0): `marketplace.gridClientMods`,
147
+ `consentGridClientMod`, `trustGridAuthor`, `clientArtifact`, `clientArtifactBytes`, and
148
+ `GridScope.compute.clientMods`.
149
+
150
+ Existing CLIENT projects keep their files: a CLIENT crate on `crowdy-compute-sdk` is refused on
151
+ ck-exec before any build, with what to change (the SDK line becomes
152
+ `crowdy-client-sdk = "0.1.0"`, `crowdy_compute_sdk` becomes `crowdy_client_sdk`; the host
153
+ calls are the same less the Game Model, sessions and grid state, `grid_state_get` /
154
+ `grid_state_set`, which a mod's server half holds now). `serverEngine: 'player-compute'` keeps
155
+ both targets on legacy player compute until 18.0.
156
+
157
+ **Studio's CLIENT target on ck-exec needs ck-api `v2.25.1` or later.** `v2.24.0` and
158
+ `v2.25.0` refuse to save a `crowdy-client-sdk` crate in a Studio project
159
+ (`CROWDY_STUDIO_MANIFEST_INVALID`, cks-game-api #425), so a CLIENT project cannot start there;
160
+ the SDK calls themselves need only `v2.24.0`.
161
+
1
162
  # 17.13.0 ck-exec observability
2
163
 
3
164
  Additive (dev-tier preview; ck-api `v2.22.0`).
@@ -37,7 +198,8 @@ Crowdy Studio's SERVER target moves to ck-exec mods, which 17.12.0 offered behin
37
198
  package named for the project, instead of the legacy `crowdy-compute-sdk` crate; the
38
199
  server module name fits a mod's (48 characters, starting with a letter) and a full-stack
39
200
  project records pairing `NONE`, since a mod has no client pairing. The CLIENT crate is
40
- unchanged. `createCrowdyStudioStarterProject` takes the starter as `modStarter`.
201
+ unchanged (17.14.0 moved it to ck-exec). `createCrowdyStudioStarterProject` takes the
202
+ starter as `modStarter`.
41
203
  - **Invoke, Logs, Runs and the budget line follow the engine.** On ck-exec, Invoke calls the
42
204
  mod's endpoint (default `state`, JSON arguments sent as MessagePack) over one exec
43
205
  connection, and the result is the decoded reply as JSON; Logs are the mod's `ctx.log`
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  The official browser-first TypeScript SDK for **Crowded Kingdoms**. CrowdyJS
4
4
  gives you typed clients for the whole platform: identity and
5
- studio administration (the Management surface), world data and the abstract
6
- game model (the Game surface), and the UDP proxy realtime stream — all over
7
- one unified GraphQL API.
5
+ studio administration (the Management surface), world data (the Game surface),
6
+ the UDP proxy realtime stream, and ck-exec, where an app's server code runs —
7
+ all behind one unified GraphQL API.
8
8
 
9
9
  Authentication follows a two-token model: an identity **session token** for
10
10
  account and admin operations, and short-lived **app-scoped tokens** for
@@ -225,23 +225,19 @@ never told about, so a native refresh without it is a re-placement.
225
225
  | `client.platform` | Public platform configuration (`config()`). |
226
226
  | `client.serverStatus` | `gameClientBootstrap(appId)` — per-app version info, UDP status, spatial limits. |
227
227
  | `client.chunks`, `client.voxels`, `client.actors`, `client.avatars`, `client.state` | World data reads + writes: terrain/LODs, voxel edit + history/rollback, durable actors, avatars, per-user app state blobs. |
228
- | `client.host` | Game-host election (`get`, `amIHost`) + actor liveness `heartbeat`. `amIHost` is UI convenience only — authoritative host gating uses `gameModelInvoke`'s `is_host` policy. |
228
+ | `client.host` | Game-host election (`get`, `amIHost`) + actor liveness `heartbeat`. `amIHost` is UI convenience only — a hub decides host-only actions from its caller. |
229
229
  | `client.teleport` | Teleport requests. |
230
230
  | `client.channels`, `client.teams` | Messaging channels and app-scoped player teams (membership + roles). |
231
- | `client.gameModel` | Abstract game model: containers, properties, functions (incl. model-driven `notify_*` effects), sessions, app-scoped active-session counts (`activePlayerCount`, `activePlayerCountChanged`), container-change push (`containerChanged`), flow-correlation timelines (`flow`), automations / NPCs (`upsertAutomation`, `runAutomation`, `automationRuns`, `automationStats`, …), and one-shot timers (`scheduleInvoke`, `cancelTimer`, `timers`). |
232
- | `client.compute` | Compute Modules — server-side Rust/WASM logic: author + deploy source (`upsertModule`, `deployVersion`, `deployTemplate`, `waitForCompile`), triggers + policy, synchronous `invoke`, and monitoring (`moduleRuns`, `moduleStats`, `moduleLogs`, `appDiagnostics`). See [Compute Modules](https://docs.crowdedkingdoms.com/game-api/compute-modules). |
233
- | `client.exec` | **ck-exec (dev-tier preview, 17.9.0):** `connect(appId, { nodeType, key })` opens a player's connection to an execution host; the `ExecConnection` it returns has `call`, `callRaw`, `subscribe`, `ping`, `onReconnect` and `close`, with MessagePack payloads, and it reconnects and renews subscriptions by itself. `deploy({ appId, root, types })` deploys an app's hubs and spokes (`manage_compute`). See [ck-exec](#ck-exec-dev-tier-preview). |
234
- | `client.playerCompute` | Player-authored SERVER/CLIENT Rust/WASM bound to player-owned grids: deploy source, activate/deactivate, list modules/versions, delete self-authored modules. |
235
- | `client.playerModel` | Player-owned flexible model containers and grid-confined automations (`containers`, `createContainer`, `setProperty`, `automations`, `createAutomation`, …). |
236
- | `client.playerWallet` | Player spend: balance, spend caps, card setup, policy, charges. |
237
- | `client.marketplace` | Player-code store/install/consent flows plus player-authorized grid claims (`claimGridOwnership`, `claimGridChunk`, `releaseClaimedGrid`) and client-mod artifact fetches. |
231
+ | `client.exec` | **ck-exec (dev-tier preview):** an app's server code as hubs and spokes. `connect(appId, { nodeType, key })` opens a player's connection to an execution host; the `ExecConnection` it returns has `call`, `callRaw`, `subscribe`, `ping`, `onReconnect` and `close`, with MessagePack payloads, and it reconnects and renews subscriptions by itself. `starters` / `build` / `deploy` build and deploy an app (`manage_compute`); `logs`, `instances`, `versions`, `activateVersion` and `setEnabled` operate it; `mod*` are players' mods on grids they own, and `modClient*`, `gridClientMods`, `consentClientMod` and `trustAuthor` their CLIENT halves. See [ck-exec](#ck-exec-dev-tier-preview). |
232
+ | `client.playerWallet` | Player spend: balance, ledger, hourly charges, spend caps, card setup, auto-recharge. A ck-exec mod's compute is billed here, to its owner. |
233
+ | `client.marketplace` | Player-authorized grid claims (`claimGridOwnership`, `claimGridChunk`, `releaseClaimedGrid`, requests and invites) and studio moderation of player code (admission queue, listing administration, claim policy). Mods publish and install through `client.exec`. |
238
234
  | `client.crowdyStudio` | Cloud project, personal-library, and common-file APIs for Crowdy Studio: target-scoped files, metadata/module names, optimistic revisions, copy-by-value imports, atomic saves. |
239
235
  | `client.crowdyStudioGitHub` | Optional GitHub repository for a Crowdy Studio project: status, `bind` (push the project in, or take the repository), `unbind`, `refresh`, `layout`, `tree`, `getFile`, commit-guarded `putFile` / `deleteFile`. While bound the repository is the working tree and every Studio save commits; the project's `files` are the server's mirror at `github.sha`. Never required. |
240
236
  | `client.udp` | UDP proxy subscriptions + spatial mutations (`sendActorUpdate`, `sendVoxelUpdate`, `sendAudioPacket`, `sendVideoPacket` / `sendVideoFrame`, `sendTextPacket`, `sendClientEvent`, `sendSingleActorMessage`, `sendChannelMessage`). |
241
237
  | `client.realtime` | Connection status, manual `connect()` / `disconnect()`, `onStatus()` listener. |
242
238
  | `client.refreshGameplayToken()` | Safely rotates an active game client's app token (see [Token refresh](#token-refresh-during-gameplay)). |
243
239
  | `client.world(appId)` | Higher-level helpers for browser games (`actor.join`, `actor.sendState`, `actor.sendText`, `actor.sendToActor`). |
244
- | `client.kit(appId)` | Game Kit: ready-made mappings of game concepts onto the game model — see [Game Kit](#game-kit). |
240
+ | `client.kit(appId)` | Game Kit: parties, guilds and chat over teams and channels (`kit.social`) — see [Game Kit](#game-kit). |
245
241
  | `createWorldSession(client, appId, config)` | World Stores: opt-in, SDK-managed game state from the `@crowdedkingdoms/crowdyjs/stores` subpath — see [World Stores](#world-stores). |
246
242
 
247
243
  **Studio-admin surface** (privileged; drive with a server-side / studio token,
@@ -251,7 +247,7 @@ grouped under `client.admin` and mirrored at the top level):
251
247
  |---|---|
252
248
  | `client.organizations` | Orgs, members, RBAC roles, org API tokens. |
253
249
  | `client.apps` | App registry, discovery + routing (`create`, `routeFor`, `marketplace`), visibility, and player-code admission mode / allow-list administration. |
254
- | `client.appAccess` | Access tiers + per-user grants. |
250
+ | `client.appAccess` | Access tiers + per-user grants, and the feature keys a tier grants (`defineFeature`, `grantTierFeature`, `tierFeatures`, …), which a hub checks with the node API's `players.features`. |
255
251
  | `client.billing` | Org wallet + per-app spend budgets. |
256
252
  | `client.payments` | Payment checkouts (wallet top-ups, plan purchases). |
257
253
  | `client.quotas` | Usage quotas at the org/app scope. |
@@ -259,15 +255,13 @@ grouped under `client.admin` and mirrored at the top level):
259
255
  | `client.sharedEnvironment` | Publish to shared, runtime gating, spend caps, auto-billing. |
260
256
  | `client.gameApps` | App grids (`createGrid` / `deleteGrid`), first-class grid ownership (`ownership` / `assignOwnership` / `transferOwnership`), and grid runtime-permission administration. |
261
257
 
262
- **Operator surface** (platform operations; requires `is_operator`):
258
+ There is no operator surface: `client.operator` held only the legacy compute ceilings and
259
+ went with them in 18.0.0 (ck-exec code is bounded by its manifest limits within the
260
+ platform's), and infrastructure operations live in the separate infra-control-plane service.
263
261
 
264
- | Sub-client | What it does |
265
- |---|---|
266
- | `client.operator` | Platform compute ceilings (`computePlatformCeilings`, `setComputePlatformCeilings`). Infrastructure operations live in the separate infra-control-plane service, not this SDK. |
267
-
268
- Auth, user reads and the studio-admin / operator surfaces use the **identity
269
- session token**; the world/UDP surfaces require an **app-scoped token** for that
270
- app. Both go to the same endpoint.
262
+ Auth, user reads and the studio-admin surface use the **identity session token**; the
263
+ world/UDP surfaces require an **app-scoped token** for that app. Both go to the same
264
+ endpoint.
271
265
 
272
266
  CrowdyJS wraps the full public API surface — every
273
267
  non-deprecated public root field has a typed method, with Relay `*Connection`
@@ -485,16 +479,14 @@ pre-bound — convenient for browser games. Advanced callers can always use
485
479
 
486
480
  ## Grids: player code inside one grid
487
481
 
488
- A grid is a box of chunks a player can own. Everything an app-scoped Studio
489
- module can do, code running in a grid can do too, confined to that grid:
490
- spatial messages that originate in it, the grid's own channels, a grid event
491
- bus, sessions hosted in it, and the player-tier Game Model.
482
+ A grid is a box of chunks a player can own. Code running in a grid is confined
483
+ to it: spatial messages that originate in it, the grid's own channels, and a
484
+ grid event bus. Its server side is a ck-exec mod (`client.exec.mod*`).
492
485
 
493
486
  ```ts
494
487
  const plot = client.grid(appId, gridId);
495
488
  await plot.mintToken(); // learns the box; a grid-scoped token
496
489
  await plot.channels.create('plot-chat'); // a grid channel (owner only)
497
- await plot.sessions.create({ name: 'race' }); // a game within the game
498
490
  await plot.send.text({ chunk: { x: 4, y: 0, z: 0 }, uuid, text: 'hi', distance: 2 });
499
491
  plot.send.text({ chunk: { x: 9, y: 0, z: 0 }, uuid, text: 'x' }); // throws GridScopeError
500
492
  ```
@@ -512,9 +504,17 @@ import { hostGridProgram } from '@crowdedkingdoms/crowdyjs/grid-program';
512
504
  await hostGridProgram({ port, scope: client.grid(appId, gridId), graphqlUrl, graphqlWsUrl });
513
505
  ```
514
506
 
515
- `startGridMod` runs either a Rust CLIENT mod (WASM) or a JS grid program
516
- behind one interface; `createGridHostCalls` answers every CLIENT host call in
517
- the platform catalog through CrowdyJS for a mod's broker.
507
+ `startGridMod` runs either a mod's CLIENT half (WASM) or a JS grid program
508
+ behind one interface, and `ExecClientHalves` runs a whole grid's CLIENT halves
509
+ (see [A mod's CLIENT half](#a-mods-client-half)). `createGridHostCalls` answers
510
+ a CLIENT half's host calls through CrowdyJS confined to the grid. The ones only
511
+ the game can answer need its knowledge: `local.actorsInChunk` for
512
+ `actors_list*`, `local.avatarChunk` (where an avatar's live actor is) for
513
+ `avatar_state_get`, `local.gridPermissionKeys` with the visiting player's
514
+ `userId` for `grid_permission_check`, and `local.drainPointerClicks` for
515
+ `pointer_clicks`; without its hook such a call is refused as not offered. The
516
+ Game Model, sessions and grid state calls went with the legacy engines in
517
+ 18.0.0: the broker refuses them.
518
518
 
519
519
  ## World Stores
520
520
 
@@ -571,8 +571,8 @@ The available stores are `self` (your actor + send loop), `actors` (remote
571
571
  actor registry with lanes/history/staleness), `errors` (attributed send
572
572
  errors), `chunks` (chunk/voxel cache with realtime merge + worldgen
573
573
  write-back), `channelInbox` / `actorInbox` (message inboxes), `events` (typed
574
- event router), `host` (host tracking), `save` / `avatar` (typed durable
575
- state), and `model` (game-model container mirror).
574
+ event router), `host` (host tracking), and `save` / `avatar` (typed durable
575
+ state).
576
576
 
577
577
  Every store is **opt-in twice over**: only configured stores are constructed
578
578
  (and only they exist on the session's TYPE — `session.host` without
@@ -586,101 +586,25 @@ keep timer-driven sends at full rate while hidden. See the
586
586
 
587
587
  ## Game Kit
588
588
 
589
- `client.kit(appId)` maps traditional game concepts onto the abstract game
590
- model + automations API. Studios **deploy blueprints** (the admin "load the
591
- state/rules" step, requires `manage_apps`); game clients then use the typed
592
- runtime helpers. Everything composes `client.gameModel` — no new server
593
- surface.
589
+ `client.kit(appId).social` gives parties, guilds and chat rooms in familiar
590
+ words over teams (membership and roles) and channels (messaging), with
591
+ realtime delivery on the UDP notification subscription:
594
592
 
595
593
  ```ts
596
- // Studio setup (admin context):
597
- import { inventoryBlueprint, lockBlueprint, npcBlueprint } from '@crowdedkingdoms/crowdyjs';
598
-
599
- await admin.kit(appId).deploy([
600
- inventoryBlueprint(),
601
- lockBlueprint({ objectTypeName: 'Door', authority: { kind: 'key' } }),
602
- npcBlueprint({
603
- behaviors: [{
604
- name: 'npc-wander',
605
- role: 'wanderer',
606
- trigger: { intervalMs: 60000 },
607
- mutations: [
608
- { target: 'self', property: 'x', expression: 'self.x + rand_int(-2, 2)' },
609
- { target: 'self', property: 'z', expression: 'self.z + rand_int(-2, 2)' },
610
- ],
611
- }],
612
- }),
613
- ]);
614
-
615
- // Game client (player token):
616
- const kit = game.kit(appId);
617
- const bag = await kit.inventory.ensure(me.userId);
618
- const result = await kit.objects.open(doorId, { keyId });
619
- if (!result.success) console.warn('locked:', result.errorMessage);
594
+ const kit = game.kit(appId, { social: { actorUuid: me.uuid } });
595
+ const party = await kit.social.party.create('raid'); // team + paired channel
596
+ const room = await kit.social.chat.room('lobby');
597
+ kit.social.chat.onMessage(room.groupId, (m) => log(m.senderUuid, m.text));
620
598
  ```
621
599
 
622
- Land sale closes the permission loop end to end:
623
-
624
- ```ts
625
- // Studio: sell a plot over a grid; doors on it honor the purchase automatically.
626
- await admin.kit(appId).deploy([
627
- plotBlueprint({ rentable: true }),
628
- lockBlueprint({ objectTypeName: 'PlotDoor',
629
- authority: { kind: 'chunkPermission', key: 'access', mode: 'smallest' } }),
630
- ]);
631
-
632
- // Game client: buying spends gold AND grants enforced grid access atomically.
633
- const buy = await kit.plots.buy(plotId, walletId);
634
- if (buy.success) await kit.objects.open(doorId); // has_chunk_permission passes now
635
- ```
636
-
637
- NPC blueprints can target by permissions too — e.g. a guard automation whose
638
- selector has `candidatePermissionWhere: [{ userFrom: { property: 'owner_user_id' },
639
- op: 'lacks', key: 'access', grid: { property: 'grid_id' } }]` reacts only to
640
- intruders.
641
-
642
- The kit covers the common genre staples end to end — each layer is a blueprint
643
- builder plus a typed runtime helper:
644
-
645
- | Layer | Builder → helper | Highlights |
646
- | --- | --- | --- |
647
- | Inventory | `inventoryBlueprint` → `kit.inventory` | bags/stacks, grant/consume/transfer, craft, barter |
648
- | Objects | `lockBlueprint` → `kit.objects` | lockable doors/chests with key or permission authority |
649
- | NPCs | `npcBlueprint` → `kit.npcs` | automation-driven NPC instances (spawn, runNow, enable) |
650
- | Plots | `plotBlueprint` → `kit.plots` | buy/rent land with transactional, replication-enforced grid grants |
651
- | Economy | `economyBlueprint` → `kit.economy` | multi-currency wallets, atomic shop buys, escrow trades, player market, escrowed order book (`kit.economy.orderBook`) |
652
- | Progression | `progressionBlueprint` → `kit.progression` | xp/levels via the `fn:` curve helper, skill prerequisite chains, achievements, host-gated rating |
653
- | Loot | `lootBlueprint` → `kit.loot` | weighted tables unrolled into seed-driven expressions, atomic single-claim, event-triggered drops |
654
- | Quests | `questsBlueprint` → `kit.quests` | event-automation progress, atomic claim into stack+wallet, cron daily resets, tutorial sequencing |
655
- | Combat | `combatBlueprint` → `kit.combat` | server-side damage/death, status-effect tick automation, `turnBased`/`hostSynced`, routed attacks |
656
- | Matches | `matchesBlueprint` → `kit.matches` | session lobbies/rounds/turns/scores, per-match channel + `onMatchChanged` (notify-to-pull) |
657
- | Decks | `decksBlueprint` → `kit.decks` | hidden hands via owner-visibility `card_id`, shuffle-by-position automation |
658
- | World sim | `worldsimBlueprint` → `kit.worldsim` | day/night clock with spatial notify, node regen + atomic gather, crops, wave counters, forecasts |
659
- | Social | `guildBlueprint` → `kit.social` | parties/guilds/chat over teams+channels, grid territory grants, guild hall + bank composite |
660
- | Leaderboards | `leaderboardsBlueprint` → `kit.leaderboards` | trusted keep-best submits, client-side ranking, cron seasons |
661
- | Live ops | `liveopsBlueprint` → `kit.liveops` | timed event windows and seasons |
662
- | Moderation | `moderationBlueprint` → `kit.moderation` | reports, queues, mutes |
663
- | Telemetry | `telemetryBlueprint` → `kit.telemetry` | counters and lightweight event tracking |
664
- | Monetization | `featureGate` → `kit.features` | feature keys, tier grants, `*policyExtra` gating on builders |
665
- | Abilities | — → `kit.abilities` | ability definitions, casts, loadouts |
666
- | Movement | — → `kit.movement` | movement warden configs + violation parsing |
667
- | Territory | — → `kit.territory` | control points, factions, enrollment |
668
- | Racing | — → `kit.racing` | courses, entries, possession (claim/pass/shoot) |
669
-
670
- Engine-aware layers talk to compute-module game engines when they are
671
- deployed, and degrade gracefully on model-only apps via capability detection
672
- (`kit.engines`): `kit.mobs` (refereed attacks, defs/slots, contact-damage
673
- parsing), `kit.pets` (adopt/summon/dismiss/rename), `kit.instances` (private
674
- world slices, seeded runs), `kit.director` (encounter runs),
675
- `kit.matchmaking` (queues/proposals/rating), `kit.minigames` (invoke-loop
676
- wrapper), plus engine paths on `kit.matches` / `kit.decks` /
677
- `kit.leaderboards` and the `kit/wire` pose codec + event parsers. Deploy
678
- engines alongside blueprints with `kit.deploy(blueprints, { engines })`.
679
-
680
- See the docs guides [Modeling game concepts](https://docs.crowdedkingdoms.com/game-api/modeling-game-concepts)
681
- (the underlying model + genre map) and [Game Kit](https://docs.crowdedkingdoms.com/crowdyjs/game-kit)
682
- (the SDK surface + the simulation-tier / notify-to-pull / timer / hidden-info
683
- / anti-cheat patterns).
600
+ Game rules and state are ck-exec hubs (`client.exec`, below). Two kit pieces
601
+ stand alone: the wire codecs (`encodeEnginePose` / `decodeEnginePose`, the
602
+ 48-byte engine pose, `engineLanes`, and `parseEngineEvent` with the typed
603
+ parsers for server events 77 and 90-98) for games whose hubs keep that wire,
604
+ and `runOptimisticAction`, which applies an action locally, asks a referee
605
+ (a hub endpoint, say) and rolls back on a denial. The blueprints,
606
+ `kit.deploy`, the engines and the model-backed helpers went with the game
607
+ model in 18.0.0; see [MIGRATION.md](MIGRATION.md).
684
608
 
685
609
  ## ck-exec (dev-tier preview)
686
610
 
@@ -735,6 +659,65 @@ Integers above 2^53 decode as `number` and lose precision unless you pass
735
659
  `{ decode: { useBigInt64: true } }`. The wire format is in the
736
660
  [ck-exec docs](https://docs.dev.crowdedkingdoms.com/exec/intro).
737
661
 
662
+ ### A mod's CLIENT half
663
+
664
+ A mod (a player's code on a grid they own: `modBuild`, `modDeploy`) can carry a **CLIENT
665
+ half** (17.14.0): browser WASM from one `crowdy-client-sdk` crate, which the mod's grid serves
666
+ to visitors who consent to it or trust its author. It runs in the visitor's
667
+ `PlayerCodeBroker`, in a worker with no DOM, token or network, and it is the only kind of
668
+ player module the broker runs since 18.0.0.
669
+
670
+ ```ts
671
+ // The mod's owner builds one crowdy-client-sdk crate and attaches it to the mod.
672
+ const queued = await client.exec.modClientBuild(appId, { name: 'hud', files }); // kind 'client'
673
+ const built = await client.exec.waitForModBuild(appId, queued.buildId);
674
+ const half = await client.exec.modClientDeploy(appId, gridId, 'shop', built.buildId);
675
+ ```
676
+
677
+ A visitor's page lists what the grid serves (`gridClientMods`: each CLIENT half's capability
678
+ summary and hash, its author's union and hash, and whether the player consented or trusts the
679
+ author), asks the player, consents (`consentClientMod(appId, modId, capabilityHash)`, or
680
+ `trustAuthor(appId, gridId, authorId, authorCapabilityHash)` once per author) and fetches the
681
+ module (`modClientArtifactBytes(appId, modId)`: its bytes checked against `digest`, its fuel
682
+ budget and tick interval). A stale hash is `CONFLICT`. The module is served only to a player
683
+ holding `run_client_code` who stands in the grid now, every refusal is `NOT_FOUND`, and a
684
+ player may fetch a mod's 12 times a minute, so cache it by `digest`. `ExecClientHalves` does
685
+ all of that:
686
+
687
+ ```ts
688
+ import { ExecClientHalves } from '@crowdedkingdoms/crowdyjs';
689
+
690
+ const halves = new ExecClientHalves({
691
+ exec: client.exec,
692
+ appId,
693
+ workerUrl, // the platform glue worker, same origin
694
+ onHostCall: (call) => hostCalls(call), // e.g. createGridHostCalls({ scope, client })
695
+ onPresentation: (p, mod) => hud.set({ source: mod.modId, label: mod.name, payload: p.payload }),
696
+ onLog: (line, mod) => devConsole.append(mod.name, line.level, line.message), // text, never HTML
697
+ confirm: (prompt) => window.confirm(describe(prompt)), // once per author; ask: 'mod' for each
698
+ onStopped: (mod) => hud.remove(mod.modId),
699
+ });
700
+ halves.enterGrid({ gridId, low, high }); // the grid the player stands in; null to leave
701
+ setInterval(() => void halves.refresh().catch(console.warn), 10_000);
702
+
703
+ // The page calls a running CLIENT half's handle_invoke; the reply is the module's bytes.
704
+ const reply = await halves.invoke(modId, new TextEncoder().encode('{"ask":"score"}'));
705
+ ```
706
+
707
+ Each refresh stops the CLIENT halves whose mod is gone or whose digest, capability hash or
708
+ tick interval changed, asks about the rest, and starts the consented ones in a broker. The
709
+ broker allows at most the host calls crowdy-client-sdk makes (`EXEC_CLIENT_HOST_CALLS`: no Game
710
+ Model, sessions or `grid_state_*`), offers exactly the CLIENT ABI imports
711
+ (`EXEC_CLIENT_ABI_IMPORTS`) and runs a module only with its digest, its fuel budget and
712
+ `consentedHostCalls`, the host calls of the summary the player consented to: a call outside them
713
+ is refused, even one whose name the module assembled at run time. A CLIENT half's
714
+ `crowdy::log` lines reach `onLog` at most 20 a second and 1,000 characters each (then a `warn`
715
+ counts what was dropped); `invoke` sends at most 256 KiB each way and is refused while the
716
+ module is not running. `NOT_FOUND` holds a CLIENT half back 15 s and `RATE_LIMITED` 60 s. The
717
+ grid's owner is asked about their own CLIENT halves like anyone; answer yes in `confirm` when
718
+ `prompt.authorId` is the player. See
719
+ [CLIENT halves](https://docs.dev.crowdedkingdoms.com/exec/client-halves).
720
+
738
721
  ## Hosting a game on Crowdy Games
739
722
 
740
723
  Since 17.2.0 (ck-api v2.1) a developer can publish a built static bundle to the
@@ -773,28 +756,32 @@ Crowdy Studio is the in-game SERVER/CLIENT Rust authoring surface for player
773
756
  code: cloud projects with target-scoped files, optimistic-concurrency
774
757
  autosave, draft/live/stop orchestration, and a Monaco editor backed by a local
775
758
  Rust language worker. The worker receives source files and the embedded
776
- platform index only — never a credential and never a server connection.
777
- Compiled CLIENT artifacts run through `PlayerCodeBroker`, which keeps tokens
778
- on the page, allow-lists host calls, and locally clamps chunk-targeted effects
779
- to the owned grid before the normal SDK path reaches server authorization.
780
- The host only ticks a CLIENT mod when `tickIntervalMs` is set (omit or `0`
781
- is invoke-only):
759
+ platform index (the `ckx-sdk` and `crowdy-client-sdk` symbols) only — never a
760
+ credential and never a server connection. A CLIENT half runs through
761
+ `PlayerCodeBroker`, which keeps tokens on the page, allow-lists host calls
762
+ (narrowed to what the player consented to), and locally clamps chunk-targeted
763
+ effects to the owned grid before the normal SDK path reaches server
764
+ authorization. It takes what the platform served with the module, and ticks it
765
+ only when `tickIntervalMs` is set (omit or `0` is invoke-only):
782
766
 
783
767
  ```ts
784
768
  import { PlayerCodeBroker } from '@crowdedkingdoms/crowdyjs';
785
769
 
770
+ const half = await client.exec.modClientArtifactBytes(appId, modId);
786
771
  const broker = new PlayerCodeBroker({
787
772
  workerUrl,
788
773
  grid,
789
774
  onHostCall,
790
- tickIntervalMs: 1000, // required for on_tick; omit for invoke-only
775
+ moduleName: half.name,
776
+ artifactHash: half.digest,
777
+ fuelPerDispatch: half.fuelPerDispatch,
778
+ consentedHostCalls: half.capabilitySummary.hostFunctions,
779
+ tickIntervalMs: half.tickIntervalMs, // omit for invoke-only
780
+ onLog: (line) => console.log(`[${line.moduleName}] ${line.level}: ${line.message}`),
791
781
  });
792
- await broker.start(artifactBytes);
782
+ await broker.start(half.bytes);
793
783
  ```
794
784
 
795
- The Studio embed defaults `clientTickIntervalMs` to `1000` when you do not
796
- pass one; a raw `PlayerCodeBroker` does not.
797
-
798
785
  Most games embed the ready-made shell rather than hand-rolling window chrome
799
786
  around `mountCrowdyStudio`:
800
787
 
@@ -805,7 +792,7 @@ import { createCrowdyStudioEmbed } from '@crowdedkingdoms/crowdyjs/crowdy-studio
805
792
  import workerUrl from '@crowdedkingdoms/crowdyjs/player-glue-worker?worker&url';
806
793
 
807
794
  const studio = createCrowdyStudioEmbed({
808
- client: game, // CrowdyClient: crowdyStudio, exec, playerCompute, playerWallet, crowdyStudioGitHub
795
+ client: game, // CrowdyClient: crowdyStudio, exec, playerWallet, crowdyStudioGitHub
809
796
  appId,
810
797
  gameName: 'My Game',
811
798
  suppressGameplayInput: () => pauseInput(),
@@ -828,18 +815,27 @@ fullscreen modal on narrow screens. For custom chrome, call
828
815
  `new CrowdyStudioController(options)`. New games should start SERVER-only.
829
816
  Untrusted HUD payloads always render as text, never HTML.
830
817
 
831
- **The SERVER target runs as a ck-exec mod.** The embed's `serverEngine` defaults to
832
- `'ck-exec'` when the client has `exec` (a `CrowdyClient` always does): a new project's
833
- SERVER target starts from the platform's mod starter (`client.exec.modStarter(appId)`, a
834
- `ckx-sdk` crate), Test draft and Deploy live build it with `modBuild` and deploy it to the
835
- grid as the mod `mod:<server module name>`, Invoke calls one of its endpoints (`state` by
836
- default) over an exec connection, and Logs shows its `ctx.log` lines. The CLIENT target is
837
- unchanged. `mountCrowdyStudio` and the controller take the same `serverEngine` with
838
- `mods: client.exec`; without `mods` they stay on legacy player compute, and
839
- `serverEngine: 'player-compute'` keeps it explicitly. The platform is switching legacy
840
- player compute off, so that option lasts only until it is removed.
841
-
842
- See [Crowdy Studio & player client mods](https://docs.crowdedkingdoms.com/crowdyjs/player-client-mods)
818
+ **The SERVER target runs as a ck-exec mod.** A new project's SERVER target starts from
819
+ the platform's mod starter (`client.exec.modStarter(appId)`, a `ckx-sdk` crate), Test draft
820
+ and Deploy live build it with `modBuild` and deploy it to the grid as the mod
821
+ `mod:<server module name>`, Invoke calls one of its endpoints (`state` by default) over an
822
+ exec connection, and Logs shows its `ctx.log` lines.
823
+
824
+ **The CLIENT target is that mod's CLIENT half** (17.14.0). A new CLIENT target starts from a
825
+ `crowdy-client-sdk` crate; Test draft and Deploy live build it with `modClientBuild`, attach
826
+ it to the project's mod with `modClientDeploy`, consent to it as its author (the API serves a
827
+ CLIENT half only to players who consented, its author too) and preview the served module in
828
+ the HUD layer; the preview's `crowdy::log` lines show in Logs beside the mod's own. A
829
+ CLIENT-only project's CLIENT half rides the mod named for its CLIENT module:
830
+ when the player has no mod of that name on the grid, Studio deploys the mod starter's server
831
+ half under it first and says so in the build log, and Stop switches that mod off. The
832
+ preview loads only while the player stands in the grid with `run_client_code`, and saving a
833
+ CLIENT project needs ck-api `v2.25.1` or later. A CLIENT crate still on the legacy
834
+ `crowdy-compute-sdk` is refused with what to change. The embed takes `exec` from the client;
835
+ `mountCrowdyStudio` and the controller take `mods: client.exec`. Crowdy Studio runs nothing
836
+ on legacy player compute since 18.0.0.
837
+
838
+ See [CLIENT halves](https://docs.dev.crowdedkingdoms.com/exec/client-halves), [mods](https://docs.dev.crowdedkingdoms.com/exec/mods)
843
839
  and [Embed Crowdy Studio in your game](https://docs.crowdedkingdoms.com/crowdyjs/crowdy-studio-embed).
844
840
 
845
841
  ### The Studio agent (DeepSeek Harness in the browser)