Files
2026-05-27 23:29:53 +01:00

3.4 KiB

OpenSpec Change: enterprise-gaming-api-plugin-matchmaking

  • Change ID: enterprise-gaming-api-plugin-matchmaking
  • Package: @suaveplan/api-plugin-matchmaking
  • Category: gaming/server
  • Target path: gaming/server/api-plugin-matchmaking
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/api-plugin-matchmaking.md

Summary

Deliver @suaveplan/api-plugin-matchmaking on the genesis branch from scratch at enterprise quality. The package wraps a MatchmakingQueue in a Hono HTTP plugin. The queue accepts skill-rated players, sorts by rating, and builds balanced teams using alternating-rank assignment with a configurable skill gap that widens over wait time. HTTP routes expose enqueue, dequeue, status, and queue stats. Genesis adds Zod player schemas, structured errors, OTel queue-depth gauge, injectable clock, and 100% coverage.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) MatchmakingError; MM.* codes
@suaveplan/types (workspace:^) Clock
@suaveplan/events (workspace:^) mm:enqueued, mm:match_found, mm:dequeued events
zod (catalog:forms) QueuedPlayer, MatchResult schemas
@opentelemetry/api (catalog:observability) Queue depth gauge, match counter

HTTP Routes

Method Path Description
POST /queue Enqueue player
DELETE /queue/:playerId Dequeue player
GET /queue/:playerId/status Queue position
POST /queue/tick Advance matchmaking (server-side tick)
GET /queue/stats Queue depth, avg wait time

Core logic

interface QueuedPlayer {
  playerId: string
  displayName: string
  skillRating: number
  gameMode: string
  enqueuedAt: number
}

interface MatchResult {
  matchId: string
  team1: QueuedPlayer[]
  team2: QueuedPlayer[]
  avgSkillGap: number
  createdAt: number
}

function createMatchmakingQueue(options: MatchmakingOptions): MatchmakingQueue

interface MatchmakingOptions {
  teamSize?: number            // players per team, default 5
  baseSkillGap?: number        // initial acceptable gap, default 200
  expandGapPerSecond?: number  // gap expansion rate, default 10/sec
  clock?: Clock
}

interface MatchmakingQueue {
  enqueue(player: QueuedPlayer): void
  dequeue(playerId: string): void
  findMatches(now: number): MatchResult[]
  getStatus(playerId: string): QueueStatus | undefined
  getStats(): QueueStats
}

Error codes

type MatchmakingErrorCode =
  | 'MM_PLAYER_NOT_FOUND'
  | 'MM_ALREADY_QUEUED'
  | 'MM_INVALID_GAME_MODE'
  | 'MM_QUEUE_FULL'

File layout

gaming/server/api-plugin-matchmaking/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    types/common.ts
    types/common.md
    core/
      matchmaking-queue/
        matchmaking-queue.ts
        matchmaking-queue.md
        matchmaking-queue.types.ts
        matchmaking-queue.test.ts
        index.ts
    routes/
      queue.ts
      queue.md
    plugin.ts
    plugin.md
  tests/
    integration.test.ts

Impact

Downstream: @suaveplan/api-plugin-game-server creates sessions for matched groups. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events.