GS2-Realtime

リアルタイム通信機能

ゲームプレイヤー同士での対戦機能を実現するために、低レイテンシーかつ高頻度で通信する場合に利用できる機能です。

GS2 では一般的にAPIリクエスト回数に対して料金が発生しますが、このサービスについてはゲームサーバーが起動すれば、 そのゲームサーバーの起動時間と、通信容量に対して費用が発生し、通信回数に対しては費用が発生しません。

graph TD
  Match["GS2-Matchmaking で<br/>ロビーを成立"] --> Want["RoomWant でルーム要求"]
  Want -- ホットスタンバイから割り当て --> Warm["Warm Start (1〜3秒)"]
  Want -- 新規起動 --> Cold["Cold Start (40〜60秒)"]
  Warm --> Connect["プレイヤーが接続"]
  Cold --> Connect
  Connect --> Play["パケットリレーで対戦中通信"]
  Play -- 1分間無通信または3時間経過 --> Shutdown["ルーム終了"]

サーバータイプ

現在、GS2-Realtime はパケットリレー機能のみを提供しています。 これはゲームサーバーにメッセージを送信すると、そのメッセージが他のプレイヤーにブロードキャストされる機能を基本として プレイヤーIDを指定して、指定したプレイヤーにメッセージを届ける機能のみを有します。

RPCやオブジェクト同期といった機能はSDKには含まれておらず、単純なバイナリデータの送受信のみを行えます。 ペイロードのバイナリエンコーディングはアプリケーション側の責務となるため、Protocol Buffers / FlatBuffers / MessagePack といった任意のエンコーディングを採用できます。

Unreal Engine Dedicated Server ホスティング

開発者が Unreal Engine でビルドした Dedicated Server をホスティングできるようにすることを検討しています。 この機能の提供時期については未定です。

有償サポート契約をしたうえで開発を勧められる、強いニーズがあればスケジュールを調整できますので、ご相談ください。

サーバースペック

サーバーの性能を設定できます。性能によって1分あたりの利用料金に差が生じます。 最も安い realtime1.nano で 8人プレイヤーが1秒間に3回メッセージを送り合えることを確認しています。

検討中の Unreal Engine Dedicated Server ホスティング では、要求スペックがこれより高くなることが予想されます。 開発効率ではなくコスト効率が最も重要なプロジェクトでは、Unreal Engine Dedicated Server の利用は推奨できません。

サーバースペックは Namespace ごとに設定し、serverSpec フィールドに realtime1.nano のような識別子を指定します。

ライフサイクル

ゲームサーバーは起動リクエストを受け付けてから手配が開始されます。

このとき、頻繁に起動リクエストが発生するゲームサーバーについては、GS2がホットスタンバイを用意します。 そのため、起動リクエストを受け付けた直後に割り当てが行われることが期待できますが、頻繁に起動リクエストが発生しない小規模なゲームでは起動リクエストから実際の割り当てまで時間がかかります。

ホットスタンバイからの割り当てを行うケースをウォームスタート、新しくサーバーを起動して割り当てるケースをコールドスタートと呼んでいます。 コールドスタート時の割り当てに必要な目安の時間は40秒〜60秒程度、ウォームスタート時の割り当てに必要な時間は1秒〜3秒を想定してください。

頻繁な起動リクエストが発生しない規模のタイトルで、コールドスタートの時間が許容できない場合 毎月固定費用が発生する前提でホットスタンバイを提供できます。ホットスタンバイの契約は5台から契約を受け付けています。

ホットスタンバイを契約していたり、頻繁な起動リクエストが発生している状況でもコールドスタートが発生しないことを保証できません。 ホットスタンバイを用意するより早くサーバーの起動リクエストを出したり、ゲームサーバーのバージョンアップ時にはコールドスタートが発生する可能性があります。 そのため、ワーストケースとしてコールドスタートが発生する状況を念頭にシステムを設計してください。

スタートの種類

種類 所要時間 説明
ウォームスタート 1〜3秒 ホットスタンバイから即時割り当て
コールドスタート 40〜60秒 新しくサーバープロセスを起動

ルームの終了条件

起動されたルームは以下の条件で終了されます。

  • ルーム作成後 5分間 誰も接続に来なかった場合
  • ルームに参加する全プレイヤーから 1分間 無通信状態が続いた場合
  • ルームの最大起動時間である 3時間 が経過した場合

加えて、明示的に RoomShutdown を呼び出すことで、ルームを即時終了させることもできます。

なお、ホットスタンバイのルームにも同様の終了処理が適用されますが、ホットスタンバイの起動時間は利用料金に加算されません。

ルームへの接続

GS2-Realtime は以下のフローで接続します。

sequenceDiagram
  participant Player
  participant Realtime as GS2-Realtime
  participant Server as GameServer
  Player ->> Realtime: Room(roomName).Model()
  Realtime -->> Player: IpAddress / Port / EncryptionKey
  Player ->> Server: RelayRealtimeSession.ConnectAsync()
  Server -->> Player: 接続完了
  Note over Player,Server: 以降はゲームサーバーと直接 WebSocket 通信

接続情報は以下の3つで構成されます。

  • IpAddress: ゲームサーバーの IP アドレス
  • Port: 接続先ポート番号
  • EncryptionKey: メッセージペイロードの暗号化キー

接続時にはアクセストークンとプレイヤーのプロフィール初期値を渡します。

プロフィール

リレーサーバーは受け取ったメッセージを他プレイヤーに伝搬するだけでなく、プレイヤーごとにプロフィールデータを持つことができます。

新しいプレイヤーがゲームサーバーに接続した時に参加中の全ての他プレイヤーのプロフィールデータを受け取ることができます。 プレイヤーの基本情報を格納しておくことで、新規プレイヤーが接続してきた時の処理を簡略化することができます。

プロフィールを更新すると、他プレイヤーにその内容が送信されるためメッセージ送信の代わりにも使えなくはありませんが、 更新時にペイロード全体が送受信されるプロフィール更新で高頻度のメッセージングを行うのは通信効率的に最適ではありません。

プロフィールはバイナリペイロードとして任意のフォーマットでエンコードできます。 プレイヤーのキャラクター情報、装備、レベル、ゲーム内通貨などのスナップショットを格納するのに適しています。

イベントハンドリング

RelayRealtimeSession には以下のイベントハンドラを設定できます。

ハンドラ 発火タイミング
OnJoinPlayer 他プレイヤーがルームへ参加した時
OnLeavePlayer 他プレイヤーがルームから退出した時
OnRelayMessage 他プレイヤーからリレーメッセージを受信した時
OnUpdateProfile 他プレイヤーがプロフィールを更新した時
OnError プロトコルエラーが発生した時
OnGeneralError 通信エラーが発生した時
OnClose ゲームサーバーとの接続が切断された時

各イベントには MessageMetadata を含む拡張版(OnRelayMessageWithMetadata 等)も用意されており、メッセージのメタ情報を併せて取得できます。

プッシュ通知

設定できる主なプッシュ通知と設定名は以下の通りです。

  • createNotification: ルーム割り当て時に通知

enableTransferMobileNotification を有効にすると、オフライン端末へのモバイルプッシュ転送にも対応します。

GS2-Matchmaking で集まったメンバー全員にルーム割り当てを通知することで、対戦の準備完了をリアルタイムにアナウンスできます。

マスターデータ運用

GS2-Realtime はマスターデータを必要としません。 ネームスペースの設定(サーバータイプ・サーバースペック・プッシュ通知設定)のみで運用できます。

トランザクションアクション

GS2-Realtime ではトランザクションアクションを提供していません。

他マイクロサービスとの連携

GS2-Realtime は単独で利用するよりも、以下のマイクロサービスと組み合わせて使うのが一般的です。

サービス 用途
GS2-Matchmaking 対戦相手をマッチングし、成立後に RoomWant を呼び出す
GS2-Gateway プッシュ通知の経路として createNotification を利用
GS2-Account プレイヤーのアクセストークンを発行

実装例

ゲームサーバーへの接続情報を取得

ルーム名を指定して接続先情報を取得します。 RoomWant を経由してマッチメイク経由でルームが用意されている場合は、その roomName を指定します。

    var item = await gs2.Realtime.Namespace(
        namespaceName: "namespace-0001"
    ).Room(
        roomName: "room-0001"
    ).ModelAsync();

    var ipAddress = item.IpAddress;
    var port = item.Port;
    var encryptionKey = item.EncryptionKey;
    const auto Domain = Gs2->Realtime->Namespace(
        "namespace-0001" // namespaceName
    )->Room(
        "room-0001" // roomName
    );
    const auto Future = Domain->Model();
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
    const auto Result = Future->GetTask().Result();

    const auto IpAddress = Result->GetIpAddress();
    const auto Port = Result->GetPort();
    const auto EncryptionKey = Result->GetEncryptionKey();

ゲームサーバーへ接続

    using (
      var session = new RelayRealtimeSession(
         accessToken, // アクセストークン
         ipAddress, // ゲームサーバのIPアドレス
         port, // ゲームサーバのポート
         encryptionKey, // 暗号鍵
         ByteString.CopyFromUtf8("my profile") // プロフィールの初期値
      )
    )
    {
      // イベントハンドラを設定

      await session.ConnectAsync();

      // セッションが有効なスコープ
    }
    // Unreal Engine 向け Realtime SDK は現在準備中です。

他プレイヤーの接続をハンドリング

    session.OnJoinPlayer += player => {
        var connectionId = player.ConnectionId;
        var profile = player.Profile;
        // 新規参加者の初期表示などを行う
    };
    // Unreal Engine 向け Realtime SDK は現在準備中です。

他プレイヤーの切断をハンドリング

    session.OnLeavePlayer += player => {
        // 退出者の表示をクリーンアップする
    };
    // Unreal Engine 向け Realtime SDK は現在準備中です。

他プレイヤーからのメッセージをハンドリング

    session.OnRelayMessage += message => {
        var sourceConnectionId = message.ConnectionId;
        var payload = message.Data;
        // payload をアプリケーション層で復号・解釈
    };
    // Unreal Engine 向け Realtime SDK は現在準備中です。

他プレイヤーのプロフィール更新をハンドリング

    session.OnUpdateProfile += player => {
        // プロフィール変更を画面に反映
    };
    // Unreal Engine 向け Realtime SDK は現在準備中です。

ゲームサーバーからの切断をハンドリング

    session.OnClose += () => {
        // 接続切断時のリトライ・遷移処理
    };
    // Unreal Engine 向け Realtime SDK は現在準備中です。

プロフィールを更新

    await session.UpdateProfileAsync(
      ByteString.CopyFromUtf8("my profile2")
    );
    // Unreal Engine 向け Realtime SDK は現在準備中です。

ブロードキャストメッセージを送信

targetConnectionIds を省略すると、自身を除くルーム内の全プレイヤーへ配信されます。

    await session.SendAsync(
      ByteString.CopyFrom(0x00, 0x01, 0x02)
    );
    // Unreal Engine 向け Realtime SDK は現在準備中です。

ユニキャストメッセージを送信

targetConnectionIds に対象プレイヤーの ConnectionId を指定します。 ConnectionIdOnJoinPlayer などで受け取れます。

    await session.SendAsync(
      ByteString.CopyFrom(0x00, 0x01, 0x02),
      new uint[] { targetConnectionId1, targetConnectionId2 }
    );
    // Unreal Engine 向け Realtime SDK は現在準備中です。

設計上の注意

バイナリペイロードの設計

GS2-Realtime はバイナリデータをそのまま中継するだけで、内容を解釈しません。 そのため、以下の点はアプリケーション側で設計する必要があります。

  • メッセージ種別を識別するためのヘッダ(先頭1バイトに opcode を入れるなど)
  • シリアライズ形式(Protocol Buffers / FlatBuffers / MessagePack 等)
  • 順序保証の必要性(必要な場合はアプリケーション層でシーケンス番号を付与)

チート対策

リレーサーバーはペイロードの内容を解釈しないため、チート行為そのものを検知することはできません。 レーティングの計算などでは投票によって多数決で結果を決定します。

詳細なリファレンス