Amazon GameLiftのカスタムゲームサーバーでマルチプレイを開始するまでの基本的な流れ

2023.12.19

こんにちは、ゲームソリューション部の入井です。

Amazon GameLiftには様々な要素が存在するため、いざゲームのマルチプレイをGameLift上で試してみようと思っても、どのように設定を進めていけば良いか悩むことがあると思います。

今回の記事では、GameLiftでのマルチプレイで一般的に使われるカスタムゲームサーバーにて、ゲームセッションの作成とプレイヤーが参加するまでの流れを紹介していきます。また、カスタムゲームサーバー関連の基本的な用語についても解説します。

2種類のゲームサーバー

Amazon GameLiftで動かせるゲームサーバーの種類として、リアルタイムゲームサーバーカスタムゲームサーバーの2つがあります。

リアルタイムゲームサーバーは、Node.jsの実行環境が提供されるゲームサーバーで、ユースケースとしては既にJavaScriptによるゲームサーバースクリプトがあり、それをGameLift上で実行したい場合に利用されます。軽量かつ素早くサーバーを用意でき、あまり通信速度が求められないカードゲーム等のターンベースのマルチプレイに向いています。

カスタムゲームサーバーは、リアルタイムゲームサーバーよりもカスタマイズ性や柔軟性が高く、様々な種類のプログラミング言語やゲームエンジンが利用可能です。サーバーの実行環境としてNode.jsを利用する予定がないのであれば、基本的にこちらのゲームサーバーの使用が推奨されています。今回の記事でも、カスタムゲームサーバーでセットアップを進めていきます。

カスタムゲームサーバーの基本要素

セットアップ作業に入る前に、カスタムゲームサーバーに関係する基本的な要素について解説します。各要素は、以下の図のような関係性を持っています。

ビルド

ゲーム開発者によって開発されたゲームサーバープログラムです。サーバーの実行ファイル、アセット、設定ファイルなどを含むパッケージをアップロードすることで、GameLift上でビルドというオブジェクトとして登録されます。

ゲームサーバーは、マルチプレイにおけるプレイヤー間の情報共有や、マルチプレイ特有の処理を実行するのが役割です。ゲームクライアントがUnityやUnreal Engineで開発されている場合、基本的にゲームサーバーもそれに合わせてC#やC++で記述されます。もちろん、それ以外の言語で書くことも可能です。

Amazon GameLiftのゲームサーバープログラムとして使用する場合、専用のSDKでGameLiftと通信する処理が書かれている必要があります。

ビルドは、フリート内のインスタンスで使用されます。具体的には、ビルドを使用して作成されたAMIでインスタンスが起動します。

フリート

ゲームサーバーを実行するEC2インスタンスの集まりです。使用するインスタンスの種類、インスタンスを起動するリージョン、その他ゲームセッション関係の調整などはフリートごとに設定し、フリート内のEC2インスタンスはその設定内容に基づいて起動します。

フリートの中のEC2インスタンスは、ビルドとしてアップロードされたゲームサーバープログラムを実行し、GameLiftからのリクエストに応じて1つ以上のゲームセッションを開始し、プレイヤーからのアクセスを受け付けます。

フリートは、オートスケーリング機能を持っています。あらかじめ設定したターゲットに基づいて、フリート内のインスタンス数を増減します。プレイヤーの需要に基づいたスケーリングの他にも、CPU使用率等のメトリクスに基づいたスケーリングも設定可能です。

ゲームセッション

プレイヤーたちがゲームをプレイするための場所です。フリート内のインスタンス上で1つ以上作成され、各プレイヤーはゲームクライアントからゲームセッションへアクセスすることでマルチプレイを行います。

マルチプレイゲームが完了するとゲームセッションは終了します。終了方法として、サーバープロセスを完全に終了する方法と、プロセスは実行したままセッションをクリーンアップして次のマルチプレイに再利用する方法が選択できます。

プレイヤーセッション

プレイヤーがゲームセッションに参加するためのリクエストのことを指します。リクエスト開始時に生成され、プレイヤーのゲームセッションへの参加が完了すると終了します。

リクエストには、プレイヤーを識別するためのID等を入れて送ります。参加可能なゲームセッションがあった場合、プレイヤーはそのゲームセッションのIPアドレスやポート番号、プレイヤーを認証するためのトークン情報が入ったレスポンスを受け取ります。プレイヤーは、レスポンスで取得した情報を使って直接インスタンス内のゲームセッションにアクセスすることでマルチプレイに参加できます。

キュー

プレイヤーのゲーム参加リクエストに対し、あらかじめ設定した条件を元に適切なフリートのゲームセッションを割り当てます。ゲームセッション配置キューとも言います。

あるキューがゲームセッションを配置する先のフリートは、自由に複数選択可能です。使用しているビルドが異なるフリートを同時に選択することもできます。

フリート割り当ての条件としては、プレイヤーの平均レイテンシーや、フリートのコストの低さなどがあり、どの条件を優先するかはキュー毎に設定できます。

ゲームバックエンド

ゲームクライアントとGameLiftの間で通信の中継役となるWebサーバーです。ゲームクライアントからのセッションリクエストなどを受け付け、その内容を元にGameLift Client SDKを使ってGameLiftと必要な通信を行い、その結果をゲームクライアントに戻します。

GameLiftのベストプラクティスとして、このようなバックエンドサーバーを設置することが推奨されています。ゲームクライアントでGameLift Client SDKを使用して直接GameLiftと通信するのは、認証等のセキュリティ面で問題があるためです。

ゲームバックエンドの実行環境としては、通常Amazon EC2が使用されます。

FlexMatch(マッチメイキングシステム)

単純化のため、上の図には書いていませんし、この後のセットアップでも登場しませんが、実際のマルチプレイ環境を用意する上では非常に便利なシステムです。

ゲームバックエンドからセッションのリクエストを受け付け、ゲームセッションの手配を行い、あらかじめ設定されたマッチメイキングルールに基づいて作成されたゲームセッションにプレイヤーを振り分けます。内部的には、キューへのセッションリクエストなどを行っています。

実際にGameLiftを利用する場合、独自のマッチメイキングシステムを使用する予定が無いのであれば、工数削減のため基本的にはFlexMatchを使用することが推奨されています。

カスタムゲームサーバーでのマルチプレイ開始までの基本的な流れ

ここからは、実際にAmazon GameLiftでマルチプレイを開始するまでの手順を紹介していきます。具体的には、フリートのインスタンス内でゲームセッションを開始し、プレイヤーがそれに参加する直前までの手順です。

なお、工程をシンプルにするためゲームクライアントは使用せず、GameLiftとはCLI上でコマンドでやりとりする形になっています。

ビルド作成

最初に、フリート内で使用するビルドを用意します。

今回はビルドの内容にはこだわる必要がないため、GameLiftのマネジメントコンソールからアクセスできるサンプルゲームを使用します。下の画像のように、メニュー内のサンプルゲームを試すという項目をクリックすることで、サンプルゲームのビルドを作成する画面に遷移できます。

なお、実際にゲームサーバーのビルドを作成する手順としては、以下のようなものが用意されています。

  • aws gamelift upload-buildコマンドによるローカルディレクトリからのアップロード
  • S3バケットにサーバー関連ファイルをアップロード後、aws gamelift create-buildコマンドによるビルド作成

カスタムサーバービルドを Amazon にアップロード GameLift - アマゾン GameLift

フリート設定

続いて、ゲームサーバーを動作させるフリートを作成します。フリートはAWS CLIでもマネジメントコンソールでも作成可能になっています。今回は、マネジメントコンソールで作成していきます。

最初に、コンピューティングタイプを選択します。EC2でゲームサーバーを実行するManaged EC2か、オンプレミス等の任意のハードウェアで実行するAnywhereから選択できます。

今回はAWS上で完結させるためにManaged EC2を選択します。

フリート名についてはSampleGameFleetと適当な内容を入れておきます。

バイナリ型という項目で、ビルドスクリプトのどちらをサーバーで実行するかを選択します。前者を選択するとついてはカスタムゲームサーバー、後者を選択するとリアルタイムゲームサーバーとしてGameLiftを実行することになります。

今回は、カスタムゲームサーバーとして実行するためビルドを選択します。また、どのビルドを実行するかを選択できるため、先ほど作成したサンプルゲームビルドを設定しておきます。

フリート内のインスタンスに設定するIAMロールや、TLS証明書、メトリクスグループ等についての設定項目がありますが、今回は関係が無いのでスキップします。

ロケーション設定により、フリート内のインスタンスを何処のリージョンで起動するかを設定できます。初期値として、現在マネジメントコンソールでアクセスしているリージョンが選択されています。

ロケーションは、複数場所設定することができます。これにより、プレイヤーのレイテンシーに対応した適切なリージョンでゲームセッションを作成したり、特定のリージョンで障害が発生した時にゲームサービスが停止してしまうのを防ぐことが可能です。

今回は簡単な動作確認をするだけなので、ロケーションはap-northeast-1のみ選択して次に進みます。

フリート内で実行するインスタンスについて、スポットオンデマンドかを選べます。今回は安く使用できるスポットを選択します。また、インスタンスタイプも選べますがc5.largeにしておきました。

実際のゲーム運用でコスト削減のためにスポットインスタンスを使用する場合、インスタンス中断による障害を防ぐため、バックアップとしてオンデマンドフリートも同時作成したり、フリートによって異なるインスタンスタイプを使い分けることがベストプラクティスになっています。

Amazon GameLift ゲームセッションキューのベストプラクティス - スポットフリートを使用したキューのベストプラクティス - アマゾン GameLift

ランタイム設定では、ゲームサーバーを起動するために必要な実行ファイルの起動パスやパラメータなどを設定できます。

サンプルゲームビルドでは、以下の内容となります。(サンプルゲーム用に用意されているフリートの設定内容のコピー)

起動パス Bin64vc141.Release.Dedicated\MultiplayerSampleLauncher_Server.exe
起動パラメータ +sv_port 33435 +gamelift_start_server +gm_netsec_enable 0
同時プロセス 1

各インスタンスで同時に起動できるゲームセッション数の制限や、ポート設定、インスタンスのスケールイン時のゲームセッション保護などの設定が可能ですが、これらの内容はデフォルト値のままにします。

ここまで設定が完了するとフリートが作成されます。最初はステータスが準備中となっており、30分程度待ってアクティブ状態になったら使用可能となります。

作成完了後、フリート詳細画面のスケーリングタブを開くと、オートスケーリング関連の設定が確認できます。初期値では、最小サイズが0、目的のインスタンスが1、最大サイズが1になっています。

「目的のインスタンス」という言葉は聞き慣れないと思いますが、英語ではDesired Instanceという名前であり、EC2 Auto Scalingで言う「希望する容量」と同じ意味を持っています。つまり、初期設定では最初に必ず1インスタンス起動する設定となっています。

なお、スケーリング設定はロケーション毎に設定することが可能です。これを活用し、例えばあるグローバル展開しているマルチプレイゲームのプレイヤー層について、日本からのアクセスが多くアメリカからのアクセスが少ないという場合、日本人向けロケーションの最大サイズを多くし、アメリカ向けロケーションの最大サイズを少なめにする、といった形でインスタンス数の調整が可能になります。

キューの作成

フリートの準備中にキューを作成します。キューについても、AWS CLIとマネジメントコンソールのどちらでも作成可能です。

マネジメントコンソールで作成ボタンをクリックすると、最初に名前などを設定する画面に遷移します。

名前についてはSampleGameQueueとし、ゲームセッション配置時のタイムアウト時間はデフォルトのまま600秒、レイテンシー条件について細かな設定ができるプレイヤーレイテンシーポリシーについては今回は設定しません。

続いて、キューがゲームセッションを配置するロケーションやフリートの設定をします。今回はロケーションをすべての場所とし、フリートは先ほど作成したSampleGameFleet1つのみを設定します。

ここで複数のフリートを送信先として登録することで、キューに届いたプレイヤーからのゲーム参加リクエストに対し、条件に基づいて割り当て先を調整してくれるようになります。

割り当て先フリートを決定するための条件については、以下の画像のようにいくつかの要素から何を優先するかを並び替える形で調整できます。今回は1つのフリートしか候補が無いため、こちらの設定はデフォルトのままにしました。

デフォルトではレイテンシー、コストの優先順位が高くなっていますが、自分で設定した送信先やロケーション順序の優先順位を上げることも可能です。

キューに関係するイベントをSNSで送信する機能がありますが、今回は使用しませんでした。

どのようなイベントを送信することができるのかについては、以下のドキュメントに記載されています。

ゲームセッション配置のイベン通知を設定 - アマゾン GameLift

ここまで設定が完了するとキューが作成されます。キューについてはフリートのようなリソースの準備は無いため、作成と同時に使用が可能です。

プレイヤーのゲームセッションリクエスト

フリートのステータスがアクティブになったら、早速ゲームセッションの作成を試していきます。今回は、AWS CLIのコマンド経由でセッション関係の処理を実行します。

最初に、start-game-session-placementを実行します。これは、新規のゲームセッションの作成リクエストをキューに投入するコマンドです。対象のキューは—game-session-queue-nameオプションで指定します。ゲームセッションの最大プレイヤー人数を指定する--maximum-player-session-countと、このリクエスト自体に任意のidを付ける--placement-idオプションは必須で入力する必要があります。

aws gamelift start-game-session-placement --placement-id 1001 \
    --game-session-queue-name SampleGameQueue \
    --maximum-player-session-count 1

コマンドが成功すると、以下のようなレスポンスが得られます。StatusプロパティがPENDINGとなっており、これはリクエストがキューの中で処理待ちの状態であることを示しています。

{
    "GameSessionPlacement": {
        "PlacementId": "1001",
        "GameSessionQueueName": "SampleGameQueue",
        "Status": "PENDING",
        "GameProperties": [],
        "MaximumPlayerSessionCount": 1,
        "PlayerLatencies": [],
        "StartTime": "2023-12-15T17:16:30.936000+09:00"
    }
}

少し待機した後、リクエストの現在の状態を確認するdescribe-game-session-placementコマンドを実行してみます。

aws gamelift describe-game-session-placement --placement-id 1001

すると、StatusFULFILLEDとなりプロパティが先ほどよりも増えた状態になりました。

{
    "GameSessionPlacement": {
        "PlacementId": "1001",
        "GameSessionQueueName": "SampleGameQueue",
        "Status": "FULFILLED",
        "GameProperties": [],
        "MaximumPlayerSessionCount": 1,
        "GameSessionId": "arn:aws:gamelift:ap-northeast-1::gamesession/fleet-exxxxxxx-bd04-4d96-98e9-0a9f16c8ed15/1001",
        "GameSessionArn": "arn:aws:gamelift:ap-northeast-1::gamesession/fleet-exxxxxxx-bd04-4d96-98e9-0a9f16c8ed15/1001",
        "GameSessionRegion": "ap-northeast-1",
        "PlayerLatencies": [],
        "StartTime": "2023-12-15T17:16:30.936000+09:00",
        "EndTime": "2023-12-15T17:16:32.658000+09:00",
        "IpAddress": "11.111.222.222",
        "DnsName": "ec2-11-111-222-222.ap-northeast-1.compute.amazonaws.com",
        "Port": 33435
    }
}

FULFILLEDとはリクエストが完了しゲームセッションがインスタンスに配置されたことを表します。新しく追加されたIpAddressPortはセッションが配置されたインスタンスの情報で、GameSessionIdはその名の通りゲームセッションのIDとなります。

プレイヤーセッションリクエスト

ゲームセッションの作成が完了したので、今度はプレイヤーのゲーム参加の手続きを進めていきます。

ゲームセッションに対しての参加リクエストは、create-player-sessionコマンドによりプレイヤーセッションを作成することで行います。オプションとして、先ほどのキューへのリクエスト完了後に発行された—game-session-idと、リクエストを送るプレイヤーを表す—player-idの指定が必要となります。

aws gamelift create-player-session \
    --game-session-id arn:aws:gamelift:ap-northeast-1::gamesession/fleet-exxxxxx-bd04-4d96-98e9-0a9f16c8ed15/1001 \
    --player-id player1

コマンドが成功し、対象のゲームセッションに空きがあると、ゲームセッション内にプレイヤーの参加枠が予約され以下のようなレスポンスが得られます。

{
    "PlayerSession": {
        "PlayerSessionId": "psess-76739133-39db-415b-814c-ba667c597d8c",
        "PlayerId": "player1",
        "GameSessionId": "arn:aws:gamelift:ap-northeast-1::gamesession/fleet-exxxxxxx-bd04-4d96-98e9-0a9f16c8ed15/1001",
        "FleetId": "fleet-exxxxxxx-bd04-4d96-98e9-0a9f16c8ed15",
        "FleetArn": "arn:aws:gamelift:ap-northeast-1:99999999:fleet/fleet-exxxxxxx-bd04-4d96-98e9-0a9f16c8ed15",
        "CreationTime": "2023-12-15T17:20:58.515000+09:00",
        "Status": "RESERVED",
        "IpAddress": "11.111.222.222",
        "DnsName": "ec2-11-111-222-222.ap-northeast-1.compute.amazonaws.com",
        "Port": 33435
    }
}

プレイヤーのゲームクライアントは、DnsNameGameSessionId等の情報を元に対象のインスタンス内のゲームセッションにアクセスします。その際、PlayerSessionIdも同時に送信します。これにより、ゲームサーバーは接続してきたクライアントが先ほどプレイヤーセッションを作成したユーザーと同一の存在であることを検証することができます。これは、無関係なクライアントがゲームセッションに接続するのを防ぐ役割があります。

ゲームサーバーでのGameSessionIdの検証が完了すると、プレイヤーセッションのStatusCOMPLETEに切り替わり、GameLiftはプレイヤーの参加リクエストが無事に完了したことを把握します。

一方、プレイヤーはゲームセッション内でマルチプレイを遊び、ゲームが終わるか離脱操作を行ったタイミングでセッションとの接続を終了します。

ゲームセッションについては、ゲームサーバーからSDK経由でGameLiftへゲームの終了を通知した段階で終了します。なお、今回の手順の場合はゲームサーバー経由で終わらせることが難しいため、代わりにフリートを削除することで終了させることをおすすめします。

まとめ

Amazon GameLiftのカスタムゲームサーバーの基本要素と、カスタムゲームサーバーでマルチプレイを開始するまでの流れについて紹介しました。

上述した通り、今回の手順ではFlexMatchを使用していませんが、実際のゲーム運用の際はキューに直接指示を出すのでは無く、FlexMatch経由でアクセスするのが一般的になると思われます。