Quick Flow
매치메이킹 ticket은 "누가 어떤 조건으로 매칭을 기다리는가"를 고정해 추적하는 요청 단위입니다. 같은 파티가 버튼을 여러 번 눌러도 하나의 활성 ticket만 갖게 하고, 완료 뒤에만 입장 정보를 내보냅니다. match를 만든 일과 game session을 배치한 일은 상태를 분리해 기록합니다.
party 검증
-> ticket 생성: party version, 규칙, 지역·지연 정보, 만료 시각 고정
-> queued / searching
-> proposed: 수락이 필요한 경우
-> placing: match는 만들었고 server 자리를 찾는 중
-> completed: matchId와 입장 정보 발급
-> cancelled / timedOut / failed| 상태 | 서버가 보장할 것 | 다음 행동 |
|---|---|---|
queued | 같은 party의 활성 ticket은 하나 | 검색 시작 또는 취소 |
searching | 후보와 규칙 버전이 ticket과 일치 | match 제안 또는 계속 대기 |
proposed | 수락 대상과 deadline 고정 | 전원 수락 또는 취소 |
placing | match는 확정, session 자리를 할당 중 | 입장 정보 발급 또는 실패 |
completed | matchId와 입장 권한이 한 번만 발급 | game server 입장 |
ticket에 고정할 값
매칭 도중 party 구성, rating, 선택한 mode, 허용 지역이 바뀌면 이전 검색 결과를 그대로 써서는 안 됩니다. ticket에는 player ID 목록뿐 아니라 party version, queue 규칙 version, input rating, 지역별 지연 정보, 생성·만료 시각을 기록합니다. party가 변경되면 기존 ticket을 취소하고 새 version으로 다시 요청합니다.
client가 보낸 ticketId를 그대로 신뢰해 다른 party의 ticket을 조회하게 하지 않습니다. ticket의 owner와 요청한 authenticated player가 연결되는지, party leader만 취소할 수 있는지, 이미 terminal state인 ticket에 수락을 다시 보내지 않는지 server에서 확인합니다. 요청마다 idempotency key를 두면 retry 때문에 활성 ticket이 두 개 생기는 일을 줄일 수 있습니다.
function startMatchmaking(party: Party, request: QueueRequest) {
assertPartyReady(party, request);
const active = tickets.findActiveByPartyId(party.id);
if (active?.partyVersion === party.version && active.ruleVersion === request.ruleVersion) {
return active; // 같은 요청의 retry는 기존 ticket을 유지
}
if (active) tickets.cancel(active.id, "party_or_rule_changed");
return tickets.create({
partyId: party.id,
partyVersion: party.version,
ruleVersion: request.ruleVersion,
expiresAt: clock.after(request.maxWait),
});
}match와 placement를 섞지 않기
matchmaking은 플레이어를 팀·규칙·map 후보로 묶는 일이고, placement는 그 match를 실행할 game server process 또는 instance를 찾거나 만드는 일입니다. match는 만들었는데 server capacity가 없어 입장하지 못할 수 있으므로, matched를 곧바로 joinable로 취급하면 안 됩니다.
managed hosting 서비스도 이 경계를 유지합니다. 예를 들어 GameLift의 ticket은 검색·수락·배치·완료 상태를 구분하며, 완료된 뒤에 game session connection information을 제공합니다. 자체 server를 운영해도 matchId, instanceId, 입장 권한을 서로 다른 값으로 두면 배치 실패와 재시도를 추적하기 쉽습니다.
timeout과 취소의 결과
timeout은 단순한 UI 문구가 아니라 queue에서 검색 후보를 제거하는 terminal state입니다. placing 도중 cancel이 들어왔을 때는 placement 결과가 늦게 성공할 수 있으므로, ticket이 이미 취소됐는지 확인하고 남은 session 자리를 회수하거나 backfill 대상으로 돌리는 정책이 필요합니다.
완료 알림을 받지 못한 client는 ticketId로 현재 상태를 다시 조회할 수 있어야 합니다. push event만 믿으면 app background, network 전환, reconnect에서 결과를 잃습니다. 반대로 client poll만으로 상태를 추측하지 말고, terminal state와 만료 시각은 server 저장소에서 읽습니다.
매치가 성립했다는 사실과 player가 game server에 입장할 수 있다는 사실은 다릅니다. completed는 입장 권한과 session 상태가 함께 준비됐을 때만 전이하세요.
참고 링크
2 sources