Skip to content

Comments

chore: multiplayer cookbook patterns#4261

Merged
NathanFlurry merged 4 commits intomainfrom
02-22-chore_multiplayer_cookbook_patterns
Feb 24, 2026
Merged

chore: multiplayer cookbook patterns#4261
NathanFlurry merged 4 commits intomainfrom
02-22-chore_multiplayer_cookbook_patterns

Conversation

@NathanFlurry
Copy link
Member

Description

Please include a summary of the changes and the related issue. Please also include relevant motivation and context.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How Has This Been Tested?

Please describe the tests that you ran to verify your changes.

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4261 February 22, 2026 21:07 Destroyed
@railway-app
Copy link

railway-app bot commented Feb 22, 2026

🚅 Deployed to the rivet-pr-4261 environment in rivet-frontend

Service Status Web Updated (UTC)
website 😴 Sleeping (View Logs) Web Feb 23, 2026 at 6:51 am
mcp-hub ✅ Success (View Logs) Web Feb 23, 2026 at 6:42 am
frontend-inspector ❌ Build Failed (View Logs) Web Feb 23, 2026 at 6:41 am
frontend-cloud ❌ Build Failed (View Logs) Web Feb 23, 2026 at 6:41 am
ladle ❌ Build Failed (View Logs) Web Feb 22, 2026 at 9:08 pm

Copy link
Member Author

NathanFlurry commented Feb 22, 2026


How to use the Graphite Merge Queue

Add the label merge-queue to this PR to add it to the merge queue.

You must have a Graphite account in order to use the merge queue. Sign up using this link.

An organization admin has enabled the Graphite Merge Queue in this repository.

Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue.

This stack of pull requests is managed by Graphite. Learn more about stacking.

@claude
Copy link

claude bot commented Feb 22, 2026

PR Review: chore: multiplayer cookbook patterns

This is a large, well-structured refactor of the multiplayer game patterns example. The core change replaces token-based auth (playerToken/internalToken) with an ID-based model, removes the openWorldIndex middleman actor, introduces a two-phase pending-player reservation flow for lobby-style matchmakers, adds event-driven assignment notifications (replacing polling), and centralizes player color assignment server-side. The direction is good and the matchmaking improvements are solid.


Issues

addPlayer action in openWorldChunk is unreachable dead code

openWorldChunk defines both enterChunk and addPlayer with identical implementations. Only enterChunk is called from the client (open-world-game.ts:setPrimaryChunk). addPlayer is never called anywhere in the codebase. It should be removed.


wait-for-assignment.ts duplicated across arena/ and turn-based/

arena/wait-for-assignment.ts and turn-based/wait-for-assignment.ts are byte-for-byte identical. They should be a single shared module (e.g., frontend/utils/wait-for-assignment.ts).


resolveChunkForPosition duplicated three times

The function is copy-pasted into open-world/menu.tsx, open-world/bot.ts, and open-world/open-world-game.ts. A shared location (or config.ts) would avoid the three definitions drifting.


generatePlayerName duplicated in physics menus

physics-2d/menu.tsx and physics-3d/menu.tsx each define the same function. Minor, but easy to centralise.


party/match.ts — join tickets are never expired

The verifyJoin queue handler checks that the ticket row exists and that host_player_id matches, but there is no expiry timestamp on join_tickets. A party could be created, its host could abandon it, and the joinToken would remain valid indefinitely until the party actor is destroyed. Either add an expires_at column (like the pending-player tables do) or document the intentional omission.


ranked/match.ts — inconsistent connection-param lookup

const playerId = params.username ?? params.playerId;

The client always sends username (see ranked-game.ts:48). The params.playerId fallback exists silently and is never exercised. If this is intentional backward compatibility, add a comment; otherwise remove the dead branch to avoid confusion about what actually identifies a ranked player.


party/match.tsconnected: true field is now meaningless

Under the old model connected tracked live vs. ghost members. Under the new model members are deleted on disconnect, so the field is always true. The frontend type still includes it. Either remove the field from the snapshot or keep it with a comment explaining it is reserved for future reconnect semantics.


colorWithAlpha silently ignores non-hex colors

The palette in player-color.ts is all 6-digit hex, so this works today. But blockColor comes from chunk.blocks which holds arbitrary server-supplied strings. If any future color value (e.g., hsl(...), rgb(...), 8-digit hex) slips in, the block will be rendered at full opacity with no warning. Consider logging on an unrecognised format.


openWorldChunk.run — state migration fires every restart

The Array.isArray(c.state.blocks) conversion runs on every cold-start. That is fine for this example, but adding a blocksMigrated flag to state and gating on it would make intent explicit and avoid the repeated check.


ranked/matchmaker.ts — concurrent ticker and queue loop share DB without visible coordination

runQueueUpdateTicker is spawned via c.waitUntil and calls sendQueueUpdates → c.db.execute concurrently with the main queue-processing loop. JavaScript's single-threaded event loop prevents true data races, but reads inside sendQueueUpdates can interleave between two await points in createRankedMatch (e.g., after inserting assignments but before syncing queue sizes), meaning a client could momentarily see stale counts. For an example project this is acceptable; worth a brief comment.


turn-based/menu.tsxcancelQueue does not abort waitForAssignment

After conn.dispose() the underlying WebSocket closes, but waitForAssignment holds a live Promise and off cleanup function that references the disposed connection. The settle guard prevents double-resolution, but the timeout inside waitForAssignment will still fire after timeoutMs (120s default) and reject into the void. An AbortSignal or cancellation token passed into waitForAssignment would make the lifecycle explicit.


Nits

  • physics-3d/world.ts: c.vars.dynamicSizes is accessed in buildSnapshot but its initialization is not visible in the diff. Verify it is always populated before the first snapshot tick.
  • ranked/leaderboard.ts wraps its queue message in an updatePlayer action (actions.updatePlayer → c.queue.send) while idle/leaderboard.ts removed its action and has callers use .send() directly. The inconsistency is harmless but worth aligning.
  • Some explanatory comments removed from game logic (e.g., // Normalize direction., // Check win condition after scoring.) were useful for readers new to the code. Prefer keeping brief explanations even when the code is obvious to the author.

@NathanFlurry NathanFlurry changed the base branch from 02-21-docs_workflows to graphite-base/4261 February 23, 2026 06:40
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from d8fec45 to 03653ea Compare February 23, 2026 06:40
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4261 February 23, 2026 06:40 Destroyed
@NathanFlurry NathanFlurry marked this pull request as ready for review February 23, 2026 06:41
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 03653ea to 9ce7640 Compare February 23, 2026 06:41
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4261 February 23, 2026 06:41 Destroyed
@NathanFlurry NathanFlurry changed the base branch from graphite-base/4261 to 02-22-docs_document_queue_with_interval_pattern February 23, 2026 06:41
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 9ce7640 to f7908a3 Compare February 23, 2026 08:25
@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from 94b5564 to 5a043e0 Compare February 23, 2026 08:25
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from f7908a3 to eaa242b Compare February 23, 2026 18:22
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from eaa242b to 9b21ae7 Compare February 23, 2026 18:34
@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from 4ae8dab to 5a043e0 Compare February 23, 2026 18:34
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 9b21ae7 to 16615f2 Compare February 23, 2026 18:35
@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from 5a043e0 to 193d01e Compare February 23, 2026 18:35
@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from 193d01e to 26393b8 Compare February 24, 2026 02:40
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 16615f2 to 04ec189 Compare February 24, 2026 02:40
@NathanFlurry NathanFlurry mentioned this pull request Feb 24, 2026
11 tasks
@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from 26393b8 to dd75c7e Compare February 24, 2026 02:57
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 04ec189 to 8c9ac24 Compare February 24, 2026 02:57
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 8c9ac24 to 16615f2 Compare February 24, 2026 03:19
@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from dd75c7e to 193d01e Compare February 24, 2026 03:19
"uuid": "^12.0.0",
"vbare": "^0.0.4",
"wa-sqlite": "^1.0.0",
"@rivetkit/sqlite": "^0.1.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dependency change from 'wa-sqlite': '^1.0.0' to '@rivetkit/sqlite': '^0.1.0' requires updating the pnpm-lock.yaml file by running 'pnpm install' locally to regenerate the lockfile with the new dependency resolution.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

"dependencies": {
"@rivetkit/bare-ts": "^0.6.2",
"wa-sqlite": "^1.0.0",
"@rivetkit/sqlite": "^0.1.0",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dependency change from 'wa-sqlite': '^1.0.0' to '@rivetkit/sqlite': '^0.1.0' requires updating the pnpm-lock.yaml file by running 'pnpm install' locally to regenerate the lockfile with the new dependency resolution.

Spotted by Graphite Agent (based on CI logs)

Fix in Graphite


Is this helpful? React 👍 or 👎 to let us know.

@NathanFlurry NathanFlurry force-pushed the 02-22-docs_document_queue_with_interval_pattern branch from 193d01e to 59dff12 Compare February 24, 2026 04:01
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 16615f2 to 6e1a56c Compare February 24, 2026 04:01
Base automatically changed from 02-22-docs_document_queue_with_interval_pattern to main February 24, 2026 04:09
@NathanFlurry NathanFlurry force-pushed the 02-22-chore_multiplayer_cookbook_patterns branch from 6e1a56c to 459718a Compare February 24, 2026 04:10
@railway-app railway-app bot temporarily deployed to rivet-frontend / rivet-pr-4261 February 24, 2026 04:10 Destroyed
@NathanFlurry NathanFlurry merged commit 2720757 into main Feb 24, 2026
2 of 6 checks passed
@NathanFlurry NathanFlurry deleted the 02-22-chore_multiplayer_cookbook_patterns branch February 24, 2026 04:10
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