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

4.8 KiB

game-server — matchmaking spec delta (Wave B-5a)

ADDED Requirements

Requirement: QueueManager primary-priority queue

QueueManager SHALL maintain an in-memory priority queue keyed by an arbitrary string (typically a game mode or queue type) and prioritise entries by a documented mix of premium bonus, wait-time bonus, and an optional caller-supplied base offset.

Scenario: addToQueue accepts a valid request

  • WHEN a caller invokes addToQueue("ranked", {...validRequest})
  • THEN the request is inserted into the queue and the queue's size grows by one
  • AND the game_server.matchmaking.queue_enqueue_total counter is incremented

Scenario: addToQueue rejects when the queue is at capacity

  • GIVEN a QueueManager configured with maxQueueSize: 1
  • AND one request has been accepted
  • WHEN a second request is submitted
  • THEN addToQueue throws GameServerError(GAME_SERVER.MATCHMAKING_QUEUE_FULL)
  • AND the game_server.matchmaking.queue_reject_total{reason=full} counter is incremented

Scenario: findMatch atomically pops the top N entries

  • GIVEN three queued players
  • WHEN the caller invokes findMatch("ranked", 2)
  • THEN the top two entries (by priority) are returned in order
  • AND the queue's size shrinks by two
  • AND the wait-time histogram is sampled twice

Requirement: SkillMatcher Elo-style closest-rating selection

SkillMatcher SHALL select the candidate closest in rating to the seeker, widening the acceptance band proportional to the seeker's waiting time and respecting a configurable hard ceiling.

Scenario: closest opponent is returned

  • GIVEN a seeker rated 1500 and candidates rated 1400, 1510, 1700
  • AND the configured ratingRange is 500
  • WHEN the caller invokes findMatch(seeker, candidates)
  • THEN the matcher returns the 1510-rated candidate

Scenario: rejection on a non-finite rating

  • WHEN the caller submits a SkillEntry whose rating is NaN
  • THEN findMatch throws GameServerError(GAME_SERVER.INVALID_SKILL_SCORE)

Requirement: SpectatorManager per-session cap

SpectatorManager SHALL enforce a configurable per-session spectator cap and surface typed-error rejections on cap violation or duplicate spectator registration.

Scenario: cap rejection

  • GIVEN a SpectatorManager configured with maxSpectatorsPerSession: 1
  • AND one spectator has been added to session "s1"
  • WHEN a second spectator is added to the same session
  • THEN addSpectator throws GameServerError(GAME_SERVER.SPECTATOR_LIMIT_REACHED)

Scenario: duplicate spectator rejection

  • GIVEN a spectator "alice" already attached to session "s1"
  • WHEN addSpectator("s1", "alice", perms) is invoked again
  • THEN the call throws GameServerError(GAME_SERVER.INVALID_SPECTATOR_REQUEST)

Requirement: WinnerStaysOnManager streak tracking

WinnerStaysOnManager SHALL track the current champion, their consecutive win streak, and a FIFO challenger queue, and expose an isChampionRotationDue flag once the streak meets maxWinStreak.

Scenario: throne transfer

  • GIVEN "alice" recorded as winner against "bob"
  • WHEN the manager records "bob" as winner against "alice"
  • THEN the champion becomes "bob" with winStreak === 1
  • AND "alice" is enqueued at the back of the challenger list

Requirement: MatchmakingEngine façade

MatchmakingEngine SHALL expose createMatch(players, criteria, algorithm) and createMatchFromQueue(queueKey, criteria, algorithm) that pick the algorithm, validate inputs via Zod, mint a stable matchId via an injected IdGenerator, and notify any registered onMatchFound listener.

Scenario: quick algorithm matches FIFO

  • GIVEN three queued players and MatchCriteria of { minPlayers: 2, maxPlayers: 4 }
  • WHEN createMatch(players, criteria, "quick") is invoked
  • THEN the engine returns a Match containing all three players

Scenario: skill-based algorithm uses the largest viable group

  • GIVEN four players with ratings 1000, 1100, 1200, 2500
  • AND MatchCriteria of { minPlayers: 2, maxPlayers: 4, skillRange: 250 }
  • WHEN createMatch(players, criteria, "skill-based") is invoked
  • THEN the engine returns the three rated 1000/1100/1200

Scenario: unknown algorithm rejection

  • WHEN the caller passes algorithm: "nuke"
  • THEN createMatch throws GameServerError(GAME_SERVER.UNKNOWN_MATCHING_ALGORITHM)

Scenario: createMatchFromQueue restores on shortfall

  • GIVEN a QueueManager with one queued player and MatchCriteria of { minPlayers: 2, maxPlayers: 4 }
  • WHEN createMatchFromQueue("ranked", criteria) is invoked
  • THEN the call returns null
  • AND the queue's size remains 1