GS2-Gateway
GS2-Gateway はゲームクライアントとサーバーとの間で WebSocket による常時接続を維持し、サーバーから任意のタイミングで通知を送るための機能を提供します。
通常のゲームサーバーとの通信はクライアントからのリクエストに対してサーバーが応答する形式ですが、GS2-Gateway を使うことでサーバー起点の通知 (メッセージ着信・フレンド申請・ギルドからの招集など) をリアルタイムにクライアントへ届けることができます。
sequenceDiagram participant Client participant Gateway as GS2-Gateway participant Service as 各マイクロサービス Client->>Gateway: WebSocket 接続 Client->>Gateway: SetUserId(認証) Service->>Gateway: SendNotification Gateway->>Client: 通知ペイロード配信
主な機能
WebSocket 常時接続
GS2-Gateway は WebSocket プロトコルでクライアントからの接続を受け付け、ユーザーごとに接続情報を保持します。 他のマイクロサービスから「このユーザーに通知を送りたい」というリクエストが届くと、接続中のクライアントに対してペイロードを配信します。
主要な連携先は以下です。
- GS2-Inbox: 新規メッセージの着信通知
- GS2-Friend: フレンド申請・承認の通知
- GS2-Guild: ギルド参加申請、ギルド内の状況変化通知
- GS2-Matchmaking: マッチング完了通知
- GS2-Distributor: トランザクション自動実行のための通知
- GS2-JobQueue: 新規ジョブが積まれた際の通知
同時接続制御
ネームスペース内のユーザーは原則として同時に1セッションのみを保持できます。
SetUserId 実行時に allowConcurrentAccess を false にすると、すでに他のセッションが接続中の場合は、新しいセッション側で同時接続エラーとして検知できます。
true にすると、新しいセッションが接続された時点で古いセッションが切断されます。
この機能を利用することで、複数デバイスからの同時ログインを抑止できます。 GS2-Account のパスワード自動変更機能と組み合わせると、アカウント共有や引き継ぎ後の旧デバイスの締め出しをより強固に行うことができます。
Firebase Cloud Messaging 連携 (モバイルプッシュ通知)
クライアントが起動していない (WebSocket が接続されていない) 状態でも通知を届けたい場合は、Firebase Cloud Messaging (FCM) との連携機能を利用できます。
GS2-Gateway は FCM HTTP v1 API で通知を送信します。
送信には GS2 がリージョンごとに保持している Google サービスアカウントを使用するため、FCM のサーバーキー (旧 firebaseSecret) を GS2 に登録する必要はありません。
旧来の FCM サーバーキーによる送信は Google 側で提供が終了しており、firebaseSecret は使用されなくなりました。
連携には以下の設定が必要です。
- ネームスペースの
firebaseProjectIdに、通知の送信元となる Firebase プロジェクトのプロジェクトIDを設定します。 - その Firebase (Google Cloud) プロジェクトで Firebase Cloud Messaging API を有効化します。
- 同じプロジェクトの IAM で、利用しているリージョンに対応する GS2 のサービスアカウントに「Firebase Cloud Messaging API 管理者」(
roles/firebasemessaging.admin) ロールを付与します。 - 各ユーザーのデバイスから取得した FCM デバイストークンを
setFirebaseTokenで GS2-Gateway に登録します。
権限を付与するサービスアカウントは、利用しているリージョンごとに以下の通りです。
| リージョン | サービスアカウント |
|---|---|
| ap-northeast-1 | api-access@gs2-ap-northeast-1-live.iam.gserviceaccount.com |
| us-east-1 | api-access@gs2-us-east-1-live.iam.gserviceaccount.com |
| eu-west-1 | api-access@gs2-eu-west-1-live.iam.gserviceaccount.com |
| ap-southeast-1 | api-access@gs2-ap-southeast-1-live.iam.gserviceaccount.com |
これは GS2-Money2 のサブスクリプション検証のために Google Play Console で権限を付与するサービスアカウントと同じものです。
GS2-Gateway は、通知配信時に WebSocket セッションが存在しないユーザーに対しては、登録済みの FCM デバイストークンを使って FCM 経由でプッシュ通知を送ります。
プッシュ通知のタイトルには通知の subject、本文には payload が設定され、必要に応じて通知音を指定できます。
issuer subject payload はデータペイロードにも含まれるため、クライアント側で通知の発生元に応じた処理を行えます。
これによりアプリ未起動時でもユーザーに通知を届けることができます。
通知エントリの enableTransferMobileNotification を有効にすることで、各通知ごとにモバイルプッシュへの転送有無を制御できます。
デバイストークンが登録されていないユーザーや、firebaseProjectId が未設定のネームスペースでは、通知の送信結果は offline となります。
アプリのアンインストールなどにより FCM からデバイストークンが無効 (未登録) であると通知された場合は、保存されているデバイストークンを自動的に削除します。
多言語のプッシュメッセージ
setFirebaseToken では、デバイストークンと合わせてプレイヤーの言語設定を locale として登録できます。
locale は省略可能で、ja や en のような任意の文字列を指定できます (固定の一覧はありません)。
通知の payload を次の形式の JSON にしておくと、登録された locale に応じてプッシュ通知の本文を出し分けられます。
{
"mobile": [
{"locale": "ja", "message": "こんにちは"},
{"locale": "en", "message": "Hello"},
{"locale": "default", "message": "Hello"}
]
}プッシュ通知の本文は以下の順序で選択されます。
localeがデバイストークンに登録されたlocaleと一致するエントリ- 見つからない場合は
localeがdefaultのエントリ - それも見つからない場合は配列の先頭のエントリ
payload を JSON として解釈できない場合や、mobile が存在しない・配列ではない・空の配列である場合は、payload をそのままプッシュ通知の本文として使用します。
message が文字列ではないエントリは無視されます。
プッシュ通知のタイトルは、この場合も常に通知の subject です。
また、データペイロードには加工前の payload (および issuer subject) がそのまま含まれるため、通知からアプリを起動した際に元の JSON を解析して利用できます。
GS2-Gateway 経由で通知を送信するサービス (GS2-Chat の投稿通知、GS2-Friend のフレンドリクエスト通知、GS2-Matchmaking の成立通知 など) は、それぞれのネームスペースに gatewayNamespaceId / enableTransferMobileNotification / sound といった通知設定を持っています。
この設定には mobileNotificationMessages を指定できます。mobileNotificationMessages は { locale, title, message } のエントリのリストで、locale は32文字以内、title は256文字以内 (省略可能)、message は1024文字以内、最大100件まで指定でき、enableTransferMobileNotification が有効な場合はマネジメントコンソールから編集できます。
通知がモバイルプッシュに転送される際は、上記と同じ規則 (デバイストークンに登録された locale → default → 配列の先頭) でエントリが選択され、message がプッシュ通知の本文、title がプッシュ通知のタイトルになります (title が空の場合は、通知の subject、たとえば Gs2Chat:Post が使用されます)。静的なテキストのみに対応しており、プレースホルダーは展開されません。
以下は GS2-Chat のネームスペースにおける通知設定の例です。
{
"gatewayNamespaceId": "grn:gs2:ap-northeast-1:YourOwnerId:gateway:default",
"enableTransferMobileNotification": true,
"mobileNotificationMessages": [
{"locale": "ja", "title": "新着メッセージ", "message": "チャットに新しいメッセージが届きました"},
{"locale": "en", "title": "New message", "message": "You have a new chat message"},
{"locale": "default", "title": "New message", "message": "You have a new chat message"}
]
}GS2-Gateway の sendNotification / sendNotificationByOwnerId / batchSendNotification (エントリごと) / sendMobileNotificationByUserId の各 API でも、同じ mobileNotificationMessages パラメータを省略可能な引数として、payload とは別に指定できます。
モバイルプッシュへの転送時の優先順位は以下の通りです。
mobileNotificationMessages(空でない場合)payloadのmobile配列- 生の
payload
ゲーム内 (WebSocket) への配信には影響しません。WebSocket の通知には payload がそのまま含まれます。
WebSocket API の設定
GS2 のクライアント SDK (Unity / Unreal Engine / Godot) には、GS2-Gateway の WebSocket 接続を自動的に確立・維持するユーティリティが組み込まれています。
ログイン時に GatewaySetting を指定することで、ログイン処理の流れに乗せて WebSocket 接続と SetUserId の呼び出しを自動的に行えます。
設定項目は以下の通りです。
gatewayNamespaceName: 使用する GS2-Gateway のネームスペース名allowConcurrentAccess: 同時接続を許可するかどうか
トランザクションアクション
GS2-Gateway ではトランザクションアクションを提供していません。
マスターデータ管理
GS2-Gateway はマスターデータを持ちません。 ネームスペースの設定で Firebase 連携情報やログ設定を構成します。
実装例
ログイン時に WebSocket 接続を確立
GatewaySetting を指定してログインすると、SDK が自動的に WebSocket セッションを確立し、SetUserId を呼び出してユーザーIDを紐付けます。
var gameSession = await gs2.LoginAsync(
new Gs2AccountAuthenticator(
accountSetting: new AccountSetting {
accountNamespaceName = this.accountNamespaceName,
},
gatewaySetting: new GatewaySetting {
gatewayNamespaceName = "namespace-0001",
allowConcurrentAccess = false,
}
),
account.UserId,
account.Password
); const auto Future = Profile->Login(
MakeShareable<Gs2::UE5::Util::IAuthenticator>(
new Gs2::UE5::Util::FGs2AccountAuthenticator(
AccountNamespaceName,
KeyId,
"namespace-0001", // gatewayNamespaceName
false // allowConcurrentAccess
)
),
UserId,
Password
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;
const auto Result = Future->GetTask().Result();var authenticator = Gs2AccountAuthenticator.new(
"account-namespace-0001",
"grn:gs2:{region}:{ownerId}:key:namespace-0001:key:key-0001",
"gateway-namespace-0001",
false
)
var game_session = Gs2GameSession.new(
authenticator, connection, account.user_id, account.password
)
var async_result = await game_session.login()
if async_result.error != null:
push_error(str(async_result.error))
return明示的にユーザーIDを設定
すでに接続している WebSocket セッションに対してユーザーIDを紐付けたい場合や、ログイン後に同時接続の許可状態を変更したい場合は、SetUserId を呼び出します。
var domain = await gs2.Gateway.Namespace(
namespaceName: "namespace-0001"
).Me(
gameSession: GameSession
).WebSocketSession(
).SetUserIdAsync(
allowConcurrentAccess: false
); const auto Future = Gs2->Gateway->Namespace(
"namespace-0001" // namespaceName
)->Me(
GameSession
)->WebSocketSession(
)->SetUserId(
false // allowConcurrentAccess
);
Future->StartSynchronousTask();
if (Future->GetTask().IsError()) return false;var domain = ez.gateway.namespace_(
"$hash"
).me(game_session).web_socket_session(
)
var async_result = await domain.set_user_id(
true, # allow_concurrent_access
null # session_id
)
if async_result.error != null:
push_error(str(async_result.error))
return
var result = async_result.resultより実践的な情報
同時接続切断のハンドリング
allowConcurrentAccess: false の設定で接続中に、別端末で同一ユーザーがログインを行うと、現在の WebSocket セッションが切断されます。
クライアントは切断イベントを検知して、再ログインを促す画面に遷移する、または「他の端末でログインされました」というメッセージを表示するといった対応を行う必要があります。
これにより、複数端末からの同時プレイを実質的に禁止する運用が可能になります。
バージョン更新時の全プレイヤー切断
GS2-Version と組み合わせ、新バージョン公開時に全プレイヤーの WebSocket セッションを切断することで、再接続のタイミングで強制的にバージョンチェックを通過させる運用が可能です。
詳細は GS2-Version の「バージョン更新の運用手順」を参照してください。