Vertical slice: chat over live 2.x server #2

Closed
opened 2026-08-19 06:49:07 +00:00 by yuris · 2 comments
Owner

Two clients connect to spacetime start + a published test module, both subscribe, one calls a reducer, both caches converge. Done = the SDK is real.

  • server/ test module (one table, two reducers) in Rust or TypeScript
  • example/ Defold project using stdb.connect
  • Verify subscribe -> reducer -> TransactionUpdate on both clients
  • Document the manual test in README

Ref: README 'Note' section.

Two clients connect to `spacetime start` + a published test module, both subscribe, one calls a reducer, both caches converge. Done = the SDK is real. - [ ] `server/` test module (one table, two reducers) in Rust or TypeScript - [ ] `example/` Defold project using `stdb.connect` - [ ] Verify subscribe -> reducer -> TransactionUpdate on both clients - [ ] Document the manual test in README Ref: README 'Note' section.
Author
Owner

Server half is done (ee90548); the Defold half is what remains.

Working today:

  • server/index.js — chat module (table message with pk, reducers send_message / clear_all), published to a local 2.8.2 server with spacetime publish --js-path, so no Rust/wasm toolchain is needed
  • full round trip verified from a Node client: subscribe -> seeded rows, reducer call -> committed insert broadcast, clear_all -> deletes, bad query -> SubscriptionError, one-off query -> response
  • the Lua decoder + cache replay that exact session in test_fixtures.lua and end in the correct state

Two module gotchas worth remembering:

  • column options are a builder chain: t.u64().primaryKey().autoInc(), not t.u64({primaryKey:true}). The options-object form silently produces a table with no pk.
  • spacetime init and destructive publish need a TTY; in scripts use spacetime login --token "$TOKEN" and pipe yes y for --delete-data.

Remaining for this issue:

  • example/ Defold project consuming the SDK as a library dependency
  • Two engine clients against the same server, seeing each other's messages
  • bob build of the example in CI (#7)
**Server half is done** (ee90548); the Defold half is what remains. Working today: - `server/index.js` — chat module (table `message` with pk, reducers `send_message` / `clear_all`), published to a local 2.8.2 server with `spacetime publish --js-path`, so no Rust/wasm toolchain is needed - full round trip verified from a Node client: subscribe -> seeded rows, reducer call -> committed insert broadcast, `clear_all` -> deletes, bad query -> `SubscriptionError`, one-off query -> response - the Lua decoder + cache replay that exact session in `test_fixtures.lua` and end in the correct state Two module gotchas worth remembering: - column options are a **builder chain**: `t.u64().primaryKey().autoInc()`, not `t.u64({primaryKey:true})`. The options-object form silently produces a table with no pk. - `spacetime init` and destructive `publish` need a TTY; in scripts use `spacetime login --token "$TOKEN"` and pipe `yes y` for `--delete-data`. Remaining for this issue: - [ ] `example/` Defold project consuming the SDK as a library dependency - [ ] Two engine clients against the same server, seeing each other's messages - [ ] `bob build` of the example in CI (#7)
Author
Owner

Closed: the SDK runs inside a real Defold engine

SMOKE: PASS cached row id=1 sender=headless

The repo is now itself a Defold library project (game.project with include_dirs = stdb), whose bootstrap collection runs main/smoke.script: connect → subscribe → call_reducer("send_message", ...) → assert the row lands in the client cache. Driven by tools/conformance.sh, which brings up the server, publishes the module, builds headless with bob, and greps the verdict.

Two bugs it caught that no unit test could:

  1. extension-websocket sends BINARY frames by default. The server then tries to BSATN-decode the JSON and drops the connection with unknown tag 0x7b for sum type ClientMessage (0x7b = {). Needs { type = websocket.DATA_TYPE_TEXT }.
  2. require("websocket") fails at build time — native extensions register globals, so it must be _G.websocket. This one breaks the build, not just runtime.

Also fixed along the way: NixOS cannot exec Defold's generic linux binaries, so the flake gained defold-patch (patchelf interpreter + rpath) with an FHS env fallback.

What is deliberately not here

The "two engine clients seeing each other's messages" part. One client proves the binding, the protocol, and the cache; a second client proves the server broadcasts, which the fixture suite already covers (TransactionUpdate frames were captured from exactly that path). Not worth a second engine process at this stage — reopen if multiplayer-specific behaviour needs it.

CI integration is tracked separately in #7 (works locally, runner environment unresolved).

## Closed: the SDK runs inside a real Defold engine `SMOKE: PASS cached row id=1 sender=headless` The repo is now itself a Defold library project (`game.project` with `include_dirs = stdb`), whose bootstrap collection runs `main/smoke.script`: connect → subscribe → `call_reducer("send_message", ...)` → assert the row lands in the client cache. Driven by `tools/conformance.sh`, which brings up the server, publishes the module, builds headless with bob, and greps the verdict. Two bugs it caught that no unit test could: 1. **`extension-websocket` sends BINARY frames by default.** The server then tries to BSATN-decode the JSON and drops the connection with `unknown tag 0x7b for sum type ClientMessage` (`0x7b` = `{`). Needs `{ type = websocket.DATA_TYPE_TEXT }`. 2. **`require("websocket")` fails at build time** — native extensions register globals, so it must be `_G.websocket`. This one breaks the *build*, not just runtime. Also fixed along the way: NixOS cannot exec Defold's generic linux binaries, so the flake gained `defold-patch` (patchelf interpreter + rpath) with an FHS env fallback. ## What is deliberately not here The "two engine clients seeing each other's messages" part. One client proves the binding, the protocol, and the cache; a second client proves the server broadcasts, which the fixture suite already covers (`TransactionUpdate` frames were captured from exactly that path). Not worth a second engine process at this stage — reopen if multiplayer-specific behaviour needs it. CI integration is tracked separately in #7 (works locally, runner environment unresolved).
yuris closed this issue 2026-08-19 08:34:36 +00:00
Sign in to join this conversation.
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/spacetimedb-defold#2
No description provided.