close
Skip to content

0.18 - #222

Open
endel wants to merge 130 commits into
masterfrom
0.18
Open

0.18#222
endel wants to merge 130 commits into
masterfrom
0.18

Conversation

@endel

@endel endel commented Jul 24, 2026

Copy link
Copy Markdown
Member

No description provided.

endel added 30 commits May 12, 2026 16:16
Documents the RoomPlugin system (definePlugins, lifecycle ordering,
message handler rules, typed state access, isolated testing) and the
first-party IdleKickPlugin shipped at colyseus/plugins/idle-kick.

Assisted-by: Claude Opus 4.7
Documents the WebRTC signaling plugin shipped at colyseus/plugins/webrtc,
including the signaling message table, client-side WebRTCClient API from
@colyseus/webrtc/client, and the maxPayload caveat for SDP-heavy
exchanges.

Assisted-by: Claude Opus 4.7
…ions

Rewrite the Room Plugins page around the array form of
`definePlugins([...])`: each plugin owns its key via
`readonly pluginName`, so `this.plugins.<key>` autocomplete works
without the user picking a name. The keyed-record form is kept as
"Multiple instances of the same plugin" for when the same class is
mounted twice.

Document `static dependencies` (auto-pull-in of prerequisite plugins,
zero-arg constructors, transitive resolution, dedup by class) and add
a warning callout against `<this>` in the `plugins =` initializer —
it triggers a TS7022 cycle and collapses autocomplete to `any`. The
canonical pattern is a type alias declared outside the room class.

Two new plugin pages: UniqueSessionPlugin (one concurrent session per
user, reject/replace) and TrackUserSessionsPlugin (per-user reverse
index in Presence — explicit install when the admin Active rooms tab
should surface a user's current room).

Assisted-by: Claude Opus 4.7
- Unify section skeleton: Features → Installation → Mounting → Usage → Password protection
- Lead with Features right after the intro on both pages
- Sentence-case headings; consistent app.config.ts code samples
- playground: drop Screenshots/Rooms wrapper, promote API endpoints to top level

Assisted-by: Claude Opus 4.7
…layer

- Rewrite pages/database.mdx around @colyseus/database (GameDatabase, services,
  matchmaker driver, custom schema), with Drizzle/BYO as the secondary fallback
- Cover dialects (SQLite default, PostgreSQL; PGlite as lower-profile option),
  migration strategies, room plugins, and feature overviews
  (auth, cloud saves, leaderboards, live configs, analytics/moderation/notes)
- Move Database from the Tools section to API Reference in the nav

Assisted-by: Claude Opus 4.7
…o-end

database.mdx:
- Promote Authentication to a top-level ### section under @colyseus/database
  with what's-wired-automatically bullets, minimal setup, admin helpers,
  and customization pointer
- Update cloud saves to the current save(userId, data, slot?, expectedVersion?)
  signature; document delete()
- Reframe "Custom schema" as "Customizing built-in table schemas" with a
  full table of the 10 built-in tables and which service uses each
- Simplify the configs Zod example to .default({}) (inner field defaults
  fill the object)

auth/module.mdx:
- Top-of-page callout pointing to the database integration
- Frame the Backend API section as the manual path

auth.mdx (landing):
- Lead with the database-backed path as the fastest route
- Add a Database-backed card alongside the existing three

auth/room.mdx:
- Callout noting JWT revocation is automatic when @colyseus/database is wired
- New "With @colyseus/database" section showing db.auth.isBanned() in onAuth

auth/http.mdx:
- Add an HTTP Routes (createEndpoint) tab — auth.middleware() works in both
  Express and createMiddleware modes
- Callout on revocation-awareness when @colyseus/database is in use

server/http-routes.mdx:
- New ### JWT Authentication subsection documenting auth.middleware() as the
  canonical JWT gate for createEndpoint, with the same revocation callout

Assisted-by: Claude Opus 4.7
Replace the icon Cards with a 5-row table mapping common questions
("How do I authenticate a room join?", "How do I gate a new HTTP route?")
to the page that answers each one. Keep the recommended-path Callout
above the table as the lede.

The Cards previously duplicated the table's destinations; the table
gives readers a one-glance "where do I go" rather than a row of
icon+title-only links.

Assisted-by: Claude Opus 4.7
@colyseus/database AuthService.ban() now folds the tokenVersion bump
into the same UPDATE as the ban fields — the package-side fix removes
the foot-gun where ban() alone left existing JWTs valid until expiry.

Doc changes reflect the new behavior:

- pages/auth.mdx: scenario 3 (revoke/force-logout) no longer says
  "bans don't bump tokenVersion — pair with bumpTokenVersion()";
  bans now invalidate live tokens on their own. Refactored /auth
  landing as an intent-based FAQ with Cards-per-scenario along the
  way (Common scenarios, 7 entries).
- pages/auth/room.mdx: top callout + "With @colyseus/database"
  section drop the pair-with-bump caveat.
- pages/auth/http.mdx, pages/server/http-routes.mdx: revocation
  callouts now name ban() alongside bumpTokenVersion() as both
  invalidating live tokens.
- pages/database.mdx: JWT-session-revocation bullet rewritten to
  describe ban() as atomic ban+bump; bumpTokenVersion() positioned
  as the standalone lever for non-ban revocation.

Assisted-by: Claude Opus 4.7
New section:
- pages/admin.mdx — landing page covering install, mount (router + Express),
  bootstrap, hardening checklist, full AdminOptions reference. Recommends
  auth.settings.onResetPassword for reset emails (player-side flow already
  handles delivery) over the panel-specific onResetRequest.
- pages/admin/authentication.mdx — sessions, JWT cookies, 3-role hierarchy,
  scoped mods, admin.guard() for gating monitor/playground/custom routes,
  password reset, rate limits.
- pages/admin/resources.mdx — defineAdminResource(), list/form/show/create,
  per-row + bulk actions, policies, FK auto-linking, audit log.
- pages/admin/dashboard.mdx — 5 preset widgets, custom widgets, 4 render
  modes (kpi/table/list/json), polling, 12-column grid.
- pages/admin/rooms.mdx — built-in room inspector (kick/lock/state edit/
  dispose), multi-process visibility, RBAC defaults.
- pages/admin/_meta.tsx — sidebar order.
- pages/_meta.tsx — Admin Panel entry in API Reference (after Database).

Reusable component:
- components/scenario-card.tsx — <ScenarioGrid> + <ScenarioCard icon title
  href>{children}</ScenarioCard> for landing-page intent indexes.
- style.css — .scenario-grid / .scenario-card with hover lift, shadow,
  150ms transitions; theme-agnostic via rgba.

Refactored:
- pages/auth.mdx — collapsed 7 vertical scenario sections into a single
  ScenarioGrid with 7 ScenarioCards.

Assisted-by: Claude Opus 4.7
Apply the new CLAUDE.md writing guideline ("Plain, approachable prose.
Skip marketing copy and hedging adverbs; keep the why alongside the
what.") across the admin section. Smaller, more surgical pass than the
previous attempt: cut marketing language ("ships with", "bundles") and
hedging adverbs ("typically", "generally"), but preserve transitional
sentences, brief rationale, and use-case examples.

Net: -/+19 lines across 5 files; CLAUDE.md committed for the first time.

Assisted-by: Claude Opus 4.7
Break pages/database.mdx (395 lines, single page) into a focused landing
plus four sub-pages — matching the cadence of the admin section.

Landing (pages/database.mdx, ~130 lines):
- Intro + service overview + beta callout
- Installation
- Quick start
- Dialects & connection strings
- Migrations
- Matchmaking driver
- Next steps (links to sub-pages)

Sub-pages:
- pages/database/authentication.mdx — built-in @colyseus/auth integration,
  admin/moderation helpers, customizing callbacks.
- pages/database/services.mdx — cloud saves, leaderboards, live configs,
  analytics/moderation/notes, plus the matching room plugins folded in
  (CloudSavesPlugin, LeaderboardsPlugin, AnalyticsPlugin).
- pages/database/schemas.mdx — customizing built-in table schemas, with
  the 10-row built-in tables reference.
- pages/database/bring-your-own.mdx — ORMs, query builders, Firebase,
  and the onAuth/onLeave integration patterns for BYO setups.
- pages/database/_meta.tsx — sidebar order.

Cross-references updated across 7 pages: /database#authentication-built-in-
colyseusauth-integration → /database/authentication;
/database#customizing-callbacks → /database/authentication#customizing-callbacks.

Assisted-by: Claude Opus 4.7
Covers the three database delivery modes (bring-your-own MMDB, MaxMind
auto-download, bundled DB-IP Lite), licensing terms for each, and
worked examples for region-locked matchmaking, locale defaults,
GDPR-aware analytics routing, and manual lookup via
`this.plugins.geoip.lookup(ip)` for reconnect-time re-resolution.

Assisted-by: Claude Opus 4.7
Replace the Cards grid with an early table of all five plugins (now
including GeoIPPlugin) with one-line descriptions; drop unused Cards import.

Assisted-by: Claude Opus 4.7
- matchmaker: add Batch Lookup by Room Id section for
  matchMaker.findRoomsByIds — single wire op for K known roomIds.

- room/plugins/track-user-sessions: document the new static
  listUserSessions(userId, options?), the UserSessionInfo shape,
  and the reconcile / removeStale options. Replace the stale
  matchMaker.query reconcile caveat.

- room/plugins/unique-session: update conflictsWith for its new
  (existing, currentRoom) signature with full IRoomCache exposure.
  Drop the misleading "scope by game mode metadata" example (it
  couldn't actually work before) and add three realistic ones:
  mode-based scoping, per-process exemption, capacity-aware
  filtering. Add a Performance section noting the 2 wire-op cap.

Assisted-by: Claude Opus 4.7
Server (room.mdx): new "Responding to a message" section — a message
handler returns a value (awaited) to answer the client; throwing settles
the request as an error; the "*" fallback can't answer and unknown types
reject with no_handler.

Client (sdk.mdx): new "Request/Response" section — room.request() and
room.send(type, payload, callback), timeout/rejection behavior, flagged
as JS/TS-only for now.

roadmap.mdx: point the #331 entry at the new section. Also folds in the
previously-staged restructure into a "Shipped in v0.18" section.

Assisted-by: Claude Opus 4.7
Add a Server-section page for the Colyseus Vite plugin (colyseus/vite),
covering setup, options, dev HMR (tied to devMode), production build, and
middleware mode. Cross-link from the Development Mode page and the server
devMode option.

Assisted-by: Claude Opus 4.7
# Conflicts:
#	style.css
New pages/netcode/ section (client-prediction, lag-compensation,
determinism, recipes) synced to the 0.18 observer-model predict API, with
targeted fixes across auth, database, faq, migrating and the phaser
tutorial surfaced by the full-site audit.

Assisted-by: Claude Opus 4.8
- sim step examples flipped to (ctx, world, command)
- room.clock section rewritten reader-first: always safe to call (no
  optional chaining), custom-clock swap note moved below the timelines
- input.data: note that undeclared-field writes do nothing and warn once
  per key with the debug panel active
- concepts.mdx removed — superseded by the netcode section

Assisted-by: Claude Opus 4.8
…cessor bookkeeping as a list

Assisted-by: Claude Opus 4.8
Migration guide gains: preview/next-tag install callout, schema 4→5 and
SDK version bumps, request/response messaging section, client.id /
fossil-delta-serializer removals, playground production lockdown.
Banner + header badge flip from 0.17 to 0.18.

Assisted-by: Claude Opus 4.8
…cipe

The three matchmaking flags were listed without semantics, and the
password recipe recommended setPrivate() — which excludes the room from
joinOrCreate() matching, defeating its own filterBy(['password']) step
(verified against the 0.18 test harness).

- room#matchmaking-properties: flag × join-path table, per-flag guidance,
  no-built-in-password callout; restore the truncated lock()/unlock()
  sections and the individual-setters list; metadata is replaced, not
  merged
- password recipe: filterBy + unlisted (not private) + onAuth() check to
  cover the joinById bypass and the missing-password-field hole
- consistency: driver/matchmaker/lobby pages now agree on what each flag
  filters

Thanks @jeffreyhugh for the heads-up!

Assisted-by: Claude Opus 4.8
- Document `onAuth` as a plugin hook: ordering row, the returns-nothing
  contract, and how GeoIPPlugin uses it to populate `client.geoip`.
- Switch examples to the array form of `definePlugins()`, the recommended
  shape; explain when the record form is worth it.
- Note that `@colyseus/geoip` is the one plugin outside the core package.
- Replace the misleading `onKick` note: arrows bind `this` to the room.
- Repoint broken links (`/auth/room`, `/sdk#send-and-receive-messages`)
  and swap `this.log.warn` for `console.warn`.

Assisted-by: Claude Opus 4.8
Tier 1 of TODO/sidebar-reorg.md — sidebar labels and ordering only, no URL
or content changes.

Root sidebar:
- "API Reference" becomes "Building Your Game", reordered to the dev loop:
  Server, Rooms, State Sync, Client SDK, Matchmaking, Auth, Database.
- Netcode moves out to its own "Advanced" separator — as the flagship 0.18
  feature it was buried at 5th-of-9, and the split signals "after the basics".
- "Matchmaker API" retitled "Matchmaking"; it was the lone "...API" entry.
- Admin Panel moves under "Tools & Integrations" (ex-"Tools") — it's a
  product, not API reference.
- "Infrastructure" renamed "Deploy & Scale".
- The consecutive "More" and "Extra" junk drawers merge into "Resources".

Server: group children under Setup / Development / Production instead of
accretion order.

Rooms: title the three untitled children and order by learning curve —
messages, timing events, reconnection, exceptions, plugins, built-in.

Assisted-by: Claude Opus 4.8
These anchors pointed at headings that no longer exist (or never did):

- /room#patchrate            -> #configuration-properties (it's a table row)
- /room#set-simulation-interval -> #game-loop, and the link text had a typo
  ("setSimiulationInterval"); renamed to setTimestep(), the current name
- /room#client               -> #client-instance
- /sdk#consume-seat-reservation -> #other-join-methods
- /sdk#join-or-create-a-room -> #join-or-create-recommended
- /sdk#join-existing-room    -> #other-join-methods

Found by slugging every heading with github-slugger (what Nextra uses) and
resolving every `](/page#anchor)` link against it. The existing check-links.js
and find-broken-links.js both split on "#" and only validate the page path,
which is why BROKEN_LINKS.md came out empty.

Assisted-by: Claude Opus 4.8
The two existing checkers (check-links.js, find-broken-links.js) were dead:
both used `require` under `"type": "module"`, so they crashed on startup.
That's why BROKEN_LINKS.md was empty — it wasn't "no broken links found",
the script never ran. Both are replaced by scripts/check-links.js.

The new checker validates anchors, not just page paths. A link to a renamed
heading still loads the page, so it never 404s and rots silently — that class
of rot accounted for every one of the 33 fixes here. Headings are slugged with
github-slugger (added as a devDependency), the same slugger Nextra uses, so
results match what ships. It also:

- checks same-page `#anchor` links and trailing-slash routes (`/sdk/#x`)
- skips fenced code, JSX/HTML comments, and inline code, so commented-out
  <Cards.Card href> entries don't register as links
- unwraps `[label](url)` in headings, so `## Self-hosting on [Vultr](…)`
  slugs as self-hosting-on-vultr
- exits non-zero, so it can gate CI

Run with `npm run check-links`.

Notable fixes: 4 tutorials pointed at a Unity heading renamed to "Running the
test server locally"; 9 links used an SDK page structure that no longer exists;
migrating/0.15 had a case-mismatched anchor (#onLeave-… vs the lowercase id).

Assisted-by: Claude Opus 4.8
Tier 2 (part 1) of TODO/sidebar-reorg.md. room.mdx drops 1,241 -> 801 lines:

- pages/room/visibility.mdx — the locked/private/unlisted flags, setMatchmaking,
  lock/unlock, and the password callout. Titled "Room Visibility & Access:
  locked, private, unlisted" so the terms people actually search for are in the
  page title. This is the answer that was invisible at an anchor inside a
  1,241-line page.
- pages/room/lifecycle.mdx — the full onCreate/onAuth/onJoin/onDrop/onReconnect/
  onLeave/onDispose reference plus the devMode and shutdown hooks.

room.mdx keeps overview, state, messages, config, communication, reconnection,
and Client Instance, with a short pointer where each extracted section was.

Old anchors: the plan called for mapping these in pages/404.mdx, but that can't
work — /room still exists, so Next serves it 200 and the 404 page never runs.
Server-side redirects can't help either, since browsers don't send the fragment.
Added <MovedAnchors> (components/moved-anchors.tsx), a small inline script on
the source page that maps the 16 moved hashes to their new homes, so external
links and bookmarks keep working.

Headings were promoted a level (### -> ##), which leaves their slugs unchanged,
so every moved anchor resolves on the new page. check-links.js now also
validates bare "/path#anchor" string literals, so the redirect map itself is
covered — verified with a negative test.

Verified: 165/165 pages build, check-links clean, and a line-by-line diff of the
original against the three resulting files shows no content lost (the only
deltas are the 16 heading promotions and 8 deliberately rewritten links).

Assisted-by: Claude Opus 4.8
endel added 9 commits August 15, 2026 23:25
An em-dash appositive is almost always a second idea, which the sentence
rules already want split out, and it reads worse for non-native readers
than the comma, colon or parentheses that replace it. `Colyseus.NoEmDash`
makes that a CI error rather than a habit, and flags spaced en-dashes and
` -- ` so the ban cannot be dodged with a lookalike.

The one permitted use is the empty-table-cell placeholder `| — |`, which
the rule misses by construction: its token needs a non-space neighbour,
and a lone dash in a cell has none.

Assisted-by: Claude Opus 5
They were committed before `.DS_Store` reached .gitignore, and git keeps
tracking a file the rule was added after. Finder rewrites them whenever
someone opens those folders, so they surfaced as phantom modifications.

Assisted-by: Claude Opus 5
Both are pulled from the fps demos, where the deciding property is that
they run without a DOM: one collision implementation serves the backend
and the browser, which is what lets prediction agree with the server.

node-three-gltf gets the build-time caveat too. Parsing a GLB at server
boot costs startup time for geometry that never changes.

Assisted-by: Claude Opus 5
All five premium prototypes and the Prediction Playground are live on Vercel
against the shared server at demos.colyseus.cloud, so the placeholder warning
on the play URLs can go and the playground gets a link readers can click.

check-links does not validate external URLs, so nothing here fails CI if a demo
host goes down. That is now noted where the URLs are defined.

Assisted-by: Claude Opus 5
The callback receives the same auth context as onAuth, so the ip resolution
order and the onAuth-only context.req are recorded alongside it.

Assisted-by: Claude Fable 5
The demo repositories moved into the colyseus org, so the MOBA and karts
links point at their canonical names instead of redirecting.

Assisted-by: Claude Fable 5
The intro promised "three different games" directly above an entry that
opens "a tour rather than a game", so it now says projects.

Assisted-by: Claude Fable 5
They are working notes, not documentation. TODO/ was also stripped from every
commit that carried it, so this branch's history was rewritten.

Assisted-by: Claude Fable 5
endel added 19 commits August 18, 2026 16:10
Readability pass over the Vale warnings: 330 down to 38, all of which
are the segmenter false positives documented in STYLE.md. AmbiguousThis
now also flags bare "This" with any common verb; "It" keeps the
narrower verb list because "It returns…" right after naming the
subject reads fine.

Assisted-by: Claude Fable 5
The v0.18 section only ever existed on this branch, so no published URL
carries either spelling of its old slug. The v0.17 entry stays: master
still serves that heading with the typo.

Assisted-by: Claude Opus 5
The social card grew from 800x418 to 950x418 so the wordmark sits centred
with both mascots' weapons clear of it. That drops it off the 1.91:1 ratio
Facebook and Twitter crop to, so feeds trim roughly 75px per side.

The hero ships as lossless webp, 255 KB down to 154 KB for identical
pixels, since the export runs with images.unoptimized. It no longer comes
from images/hero.psd, which is still saved in its Christmas state.

Assisted-by: Claude Opus 5
The page listed the sponsor wall and a single button, and never said what
sponsorship pays for. The shop was not linked anywhere in the docs at all.

Assisted-by: Claude Opus 5
The pitch lines said less than the facts they gestured at: the framework
runs the prediction, reconciliation, and rewind loops, and you supply the
step function. "Zero latency" also overstated: prediction hides the
round-trip, it doesn't remove it.

Assisted-by: Claude Fable 5
Figurative "underwater" is the kind of idiom the style guide exists to
avoid; the opener-metaphor rule stays, but this one reads better plain.
STYLE.md cited the exact phrase as its example, so the citation now
points at the lag-compensation opener instead.

Assisted-by: Claude Fable 5
The hero now carries "Multiplayer Framework" under the wordmark, as the social
card already did, which left the text h1 repeating what the artwork says.
Wrapping the image in the h1 keeps the heading text in the alt rather than
hiding it with CSS, which Google discounts, and priority stops the LCP element
from being lazy-loaded.

The markdown twins had no rule for a raw heading tag. h1-h6 now convert, and an
image-only heading resolves to its alt.

Assisted-by: Claude Opus 5
The one-liners left the differences invisible: drain() had no example at
all, and iterate vs next() only diverged in the surrounding loop the
snippets omitted.

Assisted-by: Claude Opus 5
sdk 0.18.2 merges the two per-second rate options into one time
constant in ms (smoothMs = 1000 / old value). Snippets move to the new
name in every language tab, the reconciler options gain the missing
smoothMs bullet, and the 0.18 migration page carries the conversion
rule.

Assisted-by: Claude Fable 5
The hold example's loop also gains the wasIdle guard: a frame held
through a packet gap repeats jump: true, and edge-triggered actions
must not re-fire on it.

Assisted-by: Claude Fable 5
The rename pass introduced smoothMs into every snippet without saying
what it does on lerp. A recipe covers the judder it exists for, the
speed × smoothMs sizing rule, and the draw-vs-hit boundary; the
per-attach options list gains the missing smoothMs bullet.

Assisted-by: Claude Fable 5
The options reference sat inside the Steps, pushing "Edit your room
code" below two tables. It now lives in its own Scaffolding options
section, and the steps keep a one-line pointer to it.

Assisted-by: Claude Fable 5
0.18 was never announced, so a rename between its previews is not a
migration concern for anyone coming from 0.17.

Assisted-by: Claude Fable 5
These repositories are private, so a non-sponsor who follows one gets a 404.
That is why the link was withheld; the note under the grid now explains it
instead.

Assisted-by: Claude Fable 5
The screenshot is a cropped frame from Defold's announcement video, which
showcases the Photon build of the same sample: the crop keeps gameplay only,
no browser chrome and no Photon branding.

Assisted-by: Claude Fable 5
@endel endel mentioned this pull request Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant