chore: multiplayer game examples#4203
chore: multiplayer game examples#4203NathanFlurry wants to merge 1 commit into02-13-chore_repo_ignore_openspecfrom
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
How to use the Graphite Merge QueueAdd 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. |
PR Review: Multiplayer Game PatternsThis PR has been significantly revised since the last review. The current version adds a single comprehensive `examples/multiplayer-game-patterns` example showcasing 7 matchmaking and session patterns, along with several core framework improvements to rivetkit-typescript. Core Framework Changes (Positive)`c.aborted` AliasThe new `get aborted()` getter on `ActorContext` is a clean ergonomic improvement. `while (!c.aborted)` reads significantly better than `while (!c.abortSignal.aborted)` in run loops. wa-sqlite Serializer (`db/mod.ts`)This is a genuine correctness fix. The new `serialize` promise-chain queues all database calls so concurrent action processing cannot cause re-entrant wa-sqlite calls. It correctly handles the case where an action throws (the rejection passes through to the caller while the queue continues). Port Sync in `runtime/index.ts`The fix that re-writes `config.publicEndpoint` and `config.serverless.publicEndpoint` when a random port is chosen is correct. The guard avoids clobbering explicitly-configured endpoints. `RawAccess` Re-exportImporting from `"rivetkit/db"` rather than navigating internal paths is better DX. Issues1. SQL String Interpolation Instead of Parameterized QueriesAll matchmakers use the `sqlString`/`sqlInt` helper pattern from `shared/sql.ts` instead of parameterized `?` placeholders. Now that `db.execute` routes to `db.query`/`db.run` when args are passed, the helpers are unnecessary for new code. The `sqlString` implementation is technically correct for the values used here (UUIDs and simple IDs). However, the pattern is non-idiomatic and acts as an anti-example for readers who may copy it into contexts where values are truly user-controlled. Consider migrating the examples to parameterized queries. 2. Race Condition in Turn-Based `processAcceptInvite`Two concurrent `acceptInvite` messages for the same open invite can both pass the `status !== "open"` check and create two separate matches from the same invite. The read and status update are not wrapped in a transaction. The `processJoinOpenPool` path correctly uses `withImmediateTransaction`, but `processAcceptInvite` does not. The fix is to wrap the read + update in a `BEGIN IMMEDIATE` transaction following the same pattern as `tryCreatePoolMatch`. 3. Committed Build Artifact`examples/multiplayer-game-patterns/public/assets/index-9bY3DXxE.js` is a minified build artifact committed to the repository. Build artifacts should not be committed to source control — they are regenerated from source and the hash-based filename will change on rebuild, producing diff churn. Consider adding `public/assets/` to `.gitignore` for this example. 4. Battle Royale: Disconnect Does Not Eliminate PlayerWhen a player disconnects, their entry is deleted from `state.players`, but if they were still `alive: true`, the match may not reach the win condition. If all players disconnect simultaneously, `aliveCount` reaches 0, `aliveCount <= 1` fires with `winnerPlayerId = undefined`, and the match ends without a winner. Worth handling explicitly — e.g., trigger `maybeFinish` after removal or mark the player as eliminated on disconnect. 5. Dead CodeThese functions are defined but never called:
6. `debugSetRating` in Ranked MatchmakerThis queue message type allows arbitrary ELO overrides with no authentication check. It is present to support tests, which is fine, but it should carry a prominent comment warning that it must be removed or access-gated before production use. As written, any client that can send queue messages could manipulate ratings. 7. Query-Type Heuristic in `db/mod.ts`The routing between `db.query` and `db.run` based on inspecting the first 16 characters of the query string will mis-route queries that begin with SQL comments (`-- select...` or `/* ... */`). Since the current matchmaker examples never pass args to `execute` (they all use `sqlString`/`sqlInt` interpolation), this code path is untested in practice. A more robust approach would be to accept an explicit `{ returning: boolean }` option, or to always use `db.query` and handle zero-row DML results uniformly. 8. Competitive Duo Team AssignmentFor `duo` mode (2 players per team), the round-robin `idx % teams` assignment means the oldest two players in the queue land on opposite teams. If two friends queue simultaneously expecting to be on the same team, this will split them. This may be intentional, but it warrants a comment since it is counterintuitive for the duo queue use case. Minor Notes
Test CoverageThe test suite covers all 7 patterns end-to-end including async queue-based flows (ranked, turn-based), cross-chunk movement (open world), and ELO update verification. The `waitFor` polling helper is a clean approach for testing message-queue-driven actors. The 15-second timeout is appropriate given the async nature. SummaryThe core framework changes (wa-sqlite serializer, `c.aborted`, port sync) are solid. The example demonstrates a good range of real-world multiplayer patterns with consistent structure across modes. Before merge:
Follow-up: |
1e888f3 to
2bff9d3
Compare
9c0c91a to
2759213
Compare

Description
Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
Type of change
How Has This Been Tested?
Please describe the tests that you ran to verify your changes.
Checklist: