GS2-Realtime

リアルタイム通信機能

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

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

サーバータイプ

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

RPCやオブジェクト同期といった機能はSDKには含まれておらず、単純なバイナリデータの送受信のみを行えます。

Unreal Engine Dedicated Server ホスティング

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

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

サーバースペック

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

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

ライフサイクル

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

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

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

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

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

プロフィール

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

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

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

実装例

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

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

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

    const auto IpAddress = Result->GetIpAdress();
    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();

      // セッションが有効なスコープ
    }

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

    session.OnJoinPlayer += player => {

    };

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

    session.OnLeavePlayer += player => {

    };

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

    session.OnRelayMessage += message => {

    };

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

    session.OnUpdateProfile += player => {

    };

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

    session.OnClose += () => {

    };

プロフィールを更新

    await session.UpdateProfileAsync(
      ByteString.CopyFromUtf8("my profile2")
    )

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

    await session.SendAsync(
      ByteString.CopyFrom(0x00, 0x01, 0x02)
    )

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

    await session.SendAsync(
      ByteString.CopyFrom(0x00, 0x01, 0x02),
      new uint[] { targetConnectionId1, targetConnectionId2 }
    )

詳細なリファレンス