Open5

自作ゲームのインゲーム・アウトゲームのバックエンド用メモ 〜マッチング機能編〜

わたなおわたなお

Claudeが無視するのをいい機会にとスクラップをやってみたかったので実施。
使っている言葉は間違えてたら指摘が欲しいなと、、、

やり方などを思いついた時にメモをしつつ、夜に実装する

クロード待ちにも書く

経緯

もともとAgones + Open Matchを使う予定だったけど、自作する方が良さげだったので自作することになった。

要件

管理画面の操作による複数ルーム作成と参加、ルームの削除などなどを実施できること。

わたなおわたなお

使用ツール

  • メインサーバー
    • Go(v1.24)
      • bun(ORM)
  • ゲームクライアント
    • UnrealEngine 5.6
  • 通信周り
    • Connect RPC
  • インフラ周り
    • Auth
      • Firebase
        • email
        • X
        • FaceBook
        • GameCenter
    • DB
      • PostgreSQL
      • Redis
    • K8s
      • CDK8s(Go)
  • 管理画面
    • NextJS(Bun)
      • TanStackQuery
わたなおわたなお

ちょっと生成AIにお願いする。

マッチングの設計として、ルームオーナーはなし、可能な限り入室後のイベントはリアルタイム性を重視しつつ、ゲーム的なロジックはUE側が実装するため、その辺りは気にしないでください。1vs1のマッチングで、観覧者はUEの限界値などを考えますが今は気にせずで大丈夫です。
RPCは「部屋作成、参加、離席、準備完了など」は1つにしRequestの内容で振り分け、双方Streamで実装することでリアルタイム性を担保しましょう。入室可能ルーム取得はUnaryでいきましょう。

// マッチングサービス
service MatchingService {
  // 入室可能ルーム取得
  rpc GetAvailableRooms(GetAvailableRoomsRequest) returns (GetAvailableRoomsResponse);
  
  // ルームイベント処理 (双方向Stream)
  // 部屋作成、参加、離席、準備完了などをリアルタイム処理
  rpc HandleRoomEvents(stream RoomEventRequest) returns (stream RoomEventResponse);
}
わたなおわたなお

以下、ClaudeとApple STTと共に作成(書きたくないから)
シーケンス図は手元に保管。

1. 概要

HandleRoomEventsは双方向Streamを使用したリアルタイムルーム管理機能です。
1vs1マッチングにおける部屋作成、参加、離席、準備完了の処理を行います。

2. ルームのルール

2.1 基本ルール

  • プレイヤー数: 1vs1(最大2名)
  • 観覧者: 無制限
  • 実力制限: RatingRangeによる制限
  • プライベート設定: Private/Publicの選択可能

3. シーケンス図

  • 正常フロー(ルーム作成→参加→マッチ開始)
  • 正常フロー(プレイヤーが途中で離席)
  • 正常フロー(準備完了状態の取り消し)
  • 異常フロー(3人目がルーム参加を試行)
  • 異常フロー(存在しないルームへの参加試行)
  • 異常フロー(既にルームに参加済みのプレイヤーが重複参加を試行)
  • 異常フロー(プライベートルームに権限なしで参加を試行)
  • 異常フロー(Stream接続切断時の処理)
  • 異常フロー(同時操作による競合状態)
  • 特殊フロー(2人目と3人目が同時にルーム参加を試行)
わたなおわたなお

Protoの一部。Claude、さすがやわ。
ところで、Goでoneofってどうやって実装するんやろう?

message RoomEventRequest {
  string user_id = 1;
  oneof event {
    CreateRoomEvent create_room = 2;
    JoinRoomEvent join_room = 3;
    LeaveRoomEvent leave_room = 4;
    ReadyEvent ready = 5;
  }
}

message RoomEventResponse {
  oneof event {
    RoomCreatedEvent room_created = 1;
    PlayerJoinedEvent player_joined = 2;
    PlayerLeftEvent player_left = 3;
    PlayerReadyEvent player_ready = 4;
    MatchStartedEvent match_started = 5;
    ErrorEvent error = 6;
  }
}