Conversation
…ArraySchema and MapSchema
- refId now read via the v5 `Symbol.for("$refId")` tag (v4 "~refId" kept
as fallback)
- tolerate v5's mixed Metadata shape (index→field objects + name→index
reverse entries) when deriving field names
- seed a no-op `decoder.triggerChanges` — v5 only collects (and returns)
DataChange[] when a subscriber exists, and the subscription wrapper
relies on the returned changes
- Snapshot<T> strips Schema base internals that v5 declares publicly
(`isTrackingPaused`, symbol-keyed $refId/$values), and accepts plain
Array/Map-shaped interfaces (IArray/IMap) so frontends can type props
without importing @colyseus/schema; compile-time assertions in
snapshotType.test.ts
Assisted-by: Claude Opus 4.8
encode() does not clear the encoder's dirty state — the caller must run discardChanges() after each patch (colyseus core does). Without it, the next encode() re-emits stale ops against a frozen index space, which schema v5 punishes with corrupted sequential array-splice patches (client decoded [C, C] where the server had [C]). Fixes 4 array tests. Also: RoomAvailable lost `publicAddress` in 0.18, and v5's Schema<any> generic needs an explicit cast in the bench comparison walker. Assisted-by: Claude Opus 4.8
Peer + dev deps: @colyseus/sdk ^0.18.1, @colyseus/schema ^5.0.8, @colyseus/shared-types ^0.18.1. Assisted-by: Claude Opus 4.8
New src/predict module, exported standalone and as room-bound variants
from createRoomContext({ predict }):
- usePredict — one shared Predict per room (Predict.get() constructs a
fresh instance per call), ref-counted with StrictMode-safe deferred
dispose
- useInput — the room's InputHandle (room.input() is idempotent)
- usePredictLoop — the frame driver: predict.tick(now) once per frame,
then the caller's send loop; owns rAF, or `{ external: true }` returns
drive(now) for an existing loop (e.g. R3F useFrame at priority -1)
- useReconciler — active prediction with the lifecycle handled: waits
for late spawn, recreates on instance replacement, disposes on unmount
- useAttachAll — passive smoothing as an effect (detach on unmount)
- useEventChannel — defineEvent with teardown plus reactivity: injected
callback wrappers re-render the caller, so has(key) derives work in
render code
- useEntityInstance / useSessionEntity — select decoded instances,
re-rendering only on identity change
- useLatch — the latch-then-consume tap recipe
- getSource(snapshot) — bridges useRoomState snapshots back to their
decoded instance for predict.value() reads (recorded for free during
snapshot creation via WeakMap)
Assisted-by: Claude Opus 4.8
Documents the structure-vs-motion split (snapshots at patch rate, predict reads per frame outside the render cycle) and each hook. Assisted-by: Claude Opus 4.8
"source" said where the object came from, not what you get back. The new name matches the "decoded schema instance" vocabulary used across the docs and the entity hooks. Pre-release rename — no alias kept. Assisted-by: Claude Fable 5
CHANGELOG for the 0.18 branch (predict hooks, schema v5 compat, peer dep bumps) and version bump 0.2.0 → 0.18.0 so @colyseus/react pairs with colyseus's own version numbers from here on. Assisted-by: Claude Fable 5
The IArray/IMap types and structural Snapshot branches were already absorbed into this branch (49bb64a) in extended form — concrete ArraySchema/MapSchema branches kept first for element inference, v5 internals stripped — so conflicts resolve to the branch's version. New from the PR: prepublishOnly → prepare, so git revisions (npm i colyseus/react-tools#0.18) build dist on install. Assisted-by: Claude Fable 5
Assisted-by: Claude Fable 5
"Latch" is electronics jargon; input buffering is the gamedev term for the same mechanism. Latch → InputBuffer, latch() → press(); consume() and peek() unchanged. Docs recipe wording moves to "buffer, then consume" alongside. Assisted-by: Claude Fable 5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Targets Colyseus 0.18 /
@colyseus/schemav5, adds React bindings for the new Predict netcode, and moves the package to0.18.0— from this release on,@colyseus/reactversioning tracks colyseus itself.Netcode hooks (new
src/predictmodule)React bindings for the 0.18 Predict tools (client prediction, reconciliation, remote smoothing). Every hook exists standalone (
useX(room, ...)) and room-bound viacreateRoomContext({ predict }):usePredict— one shared, ref-countedPredictper room (StrictMode-safe deferred dispose)useInput— the room'sInputHandleusePredictLoop— the frame driver; owns rAF, or{ external: true }for an existing loop (e.g. R3FuseFrame)useReconciler— active prediction with lifecycle handled (late spawn, instance replacement, dispose)useAttachAll— passive smoothing as an effectuseEventChannel— optimistic events with teardown plus reactivityuseEntityInstance/useSessionEntity— decoded-instance selection, identity-change re-renders onlyuseInputBuffer— the buffer-then-consume tap recipe (renamed fromuseLatchduring review)getSchemaInstance(snapshot)— bridges snapshots back to their decoded instance forpredict.value()reads (renamed fromgetSourceduring review — self-describing, matches the "decoded schema instance" vocabulary)Schema v5 compatibility
refIdread via v5'sSymbol.for("$refId")(v4~refIdfallback kept)Metadatashape when deriving field namesdecoder.triggerChangessodecode()returnsDataChange[]without an external subscriberSnapshot<T>strips v5's publicly-declared Schema internals and accepts plainIArray/IMapshapes so frontends can type props without importing@colyseus/schemaRelease
@colyseus/sdk^0.18.1,@colyseus/schema^5.0.8,@colyseus/shared-types^0.18.1 (0.17/v4 users stay on 0.1.x)IArray/IMapSnapshottypes (already absorbed on this branch in extended form) plusprepublishOnly→prepare, so git revisions (npm install colyseus/react-tools#0.18) builddist/on installTest plan
npx vitest run— 154 passed, 3 skipped (includes newpredictHooks.test.tsxandsnapshotType.test.tssuites)discardChanges()after eachencode())🤖 Generated with Claude Code