GS2-Gateway

WebSocket notification feature

GS2-Gateway provides the ability to maintain a persistent WebSocket connection between the game client and the server, and to deliver notifications from the server at any time.

Normal communication with a game server is a request-response model where the server responds to client requests, but GS2-Gateway enables server-initiated notifications (incoming messages, friend requests, guild invitations, etc.) to be delivered to the client in real time.

sequenceDiagram
  participant Client
  participant Gateway as GS2-Gateway
  participant Service as Each Microservice

  Client->>Gateway: WebSocket Connect
  Client->>Gateway: SetUserId (authenticate)
  Service->>Gateway: SendNotification
  Gateway->>Client: Deliver notification payload

Main features

Persistent WebSocket connection

GS2-Gateway accepts connections from clients using the WebSocket protocol and maintains connection information per user. When a request arrives from another microservice saying “I want to send a notification to this user”, the payload is delivered to the connected client.

The main integration sources are as follows.

  • GS2-Inbox: Notification for newly received messages
  • GS2-Friend: Notification for friend requests and approvals
  • GS2-Guild: Guild join requests and notifications of state changes within a guild
  • GS2-Matchmaking: Match completion notification
  • GS2-Distributor: Notifications for automatic transaction execution
  • GS2-JobQueue: Notification when a new job is queued

Concurrent connection control

In principle, each user within a namespace can hold only one session at a time.

When SetUserId is executed with allowConcurrentAccess set to false, if another session is already connected, the new session can detect this as a concurrent connection error. If set to true, the old session is disconnected at the point when a new session connects.

By using this feature, you can deter concurrent logins from multiple devices. Combined with the automatic password change feature of GS2-Account, you can more strongly prevent account sharing or access from the old device after a takeover.

Firebase Cloud Messaging integration (mobile push notifications)

If you want to deliver notifications even when the client is not running (when the WebSocket is not connected), you can use integration with Firebase Cloud Messaging (FCM).

GS2-Gateway sends notifications through the FCM HTTP v1 API. Sending is performed with the Google service account that GS2 holds for each region, so there is no need to register an FCM server key (the former firebaseSecret) with GS2. Sending with the legacy FCM server key has been discontinued by Google, and firebaseSecret is no longer used.

The following configuration is required for the integration.

  1. Set the project ID of the Firebase project that notifications are sent from in firebaseProjectId of the namespace.
  2. Enable the Firebase Cloud Messaging API in that Firebase (Google Cloud) project.
  3. In the IAM settings of the same project, grant the “Firebase Cloud Messaging API Admin” (roles/firebasemessaging.admin) role to the GS2 service account for the region you are using.
  4. Register the FCM device token obtained from each user’s device to GS2-Gateway with setFirebaseToken.

The service account to grant the role to differs by the region you are using, as follows.

RegionService account
ap-northeast-1api-access@gs2-ap-northeast-1-live.iam.gserviceaccount.com
us-east-1api-access@gs2-us-east-1-live.iam.gserviceaccount.com
eu-west-1api-access@gs2-eu-west-1-live.iam.gserviceaccount.com
ap-southeast-1api-access@gs2-ap-southeast-1-live.iam.gserviceaccount.com

This is the same service account that you grant permissions to in the Google Play Console for GS2-Money2 subscription verification.

When delivering a notification, GS2-Gateway sends a push notification via FCM using the registered FCM device token for users whose WebSocket session does not exist. The subject of the notification is set as the title of the push notification and the payload as its body, and a notification sound can be specified if needed. issuer, subject, and payload are also included in the data payload, so the client can handle the notification according to where it originated. This allows notifications to be delivered to users even when the app is not running.

By enabling enableTransferMobileNotification on a notification entry, you can control on a per-notification basis whether it is transferred to mobile push.

For users who have no device token registered, or for namespaces where firebaseProjectId is not set, the result of the notification delivery is offline. If FCM reports that a device token is invalid (unregistered), for example because the app was uninstalled, the stored device token is deleted automatically.

Multi-language push messages

With setFirebaseToken, you can register the player’s language setting as locale together with the device token. locale is optional, and any string such as ja or en can be specified (there is no fixed list of values).

If you make the payload of the notification a JSON document in the following format, the body of the push notification is switched according to the registered locale.

{
  "mobile": [
    {"locale": "ja", "message": "こんにちは"},
    {"locale": "en", "message": "Hello"},
    {"locale": "default", "message": "Hello"}
  ]
}

The body of the push notification is selected in the following order.

  1. The entry whose locale matches the locale registered with the device token
  2. If none is found, the entry whose locale is default
  3. If that is not found either, the first entry of the array

If the payload cannot be parsed as JSON, or if mobile does not exist, is not an array, or is an empty array, the payload is used as the body of the push notification as is. Entries whose message is not a string are ignored.

The title of the push notification is always the subject of the notification in this case as well. The raw payload (and issuer and subject) is also included in the data payload as is, so the original JSON can be parsed and used when the app is launched from the notification.

Services that send notifications through GS2-Gateway (such as GS2-Chat’s post notifications, GS2-Friend’s friend request notifications, and GS2-Matchmaking’s match-established notifications) have a notification setting (gatewayNamespaceId / enableTransferMobileNotification / sound) on their namespace. This setting now accepts mobileNotificationMessages: a list of { locale, title, message } entries, where locale is at most 32 characters, title is optional and at most 256 characters, message is at most 1024 characters, and up to 100 entries can be specified. It can be edited in the management console when enableTransferMobileNotification is enabled. When the notification is forwarded to mobile push, the entry is chosen by the same rule as above (the locale registered with the device token → default → the first entry). The message becomes the body of the push notification and the title becomes its title (when title is empty, the notification’s subject is used instead, for example Gs2Chat:Post). Only static text is supported; placeholders are not expanded.

The following is an example of a notification setting on a GS2-Chat namespace.

{
  "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"}
  ]
}

The GS2-Gateway APIs sendNotification / sendNotificationByOwnerId / batchSendNotification (per entry) / sendMobileNotificationByUserId also accept the same optional mobileNotificationMessages parameter, carried separately from payload.

The priority when forwarding to mobile push is as follows.

  1. mobileNotificationMessages (when it is not empty)
  2. The mobile array of the payload
  3. The raw payload

This does not affect delivery within the game (over WebSocket); the WebSocket notification carries the payload unchanged.

WebSocket API configuration

The GS2 client SDKs (Unity / Unreal Engine / Godot) include a utility that automatically establishes and maintains the GS2-Gateway WebSocket connection.

By specifying GatewaySetting at login time, the WebSocket connection and SetUserId call are performed automatically as part of the login flow.

The configuration items are as follows.

  • gatewayNamespaceName: The name of the GS2-Gateway namespace to use
  • allowConcurrentAccess: Whether to allow concurrent connections

Transaction Actions

GS2-Gateway does not provide transaction actions.

Master Data Management

GS2-Gateway does not have master data. Configuration such as Firebase integration information and log settings is set in the namespace settings.

Example Implementation

Establishing a WebSocket connection at login

By logging in with GatewaySetting specified, the SDK automatically establishes a WebSocket session and calls SetUserId to associate the user 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

Explicitly setting the user ID

If you want to associate the user ID with an already-connected WebSocket session, or if you want to change the concurrent access setting after login, call 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

More practical information

Handling concurrent connection disconnects

While connected with the setting allowConcurrentAccess: false, if the same user logs in from another device, the current WebSocket session is disconnected. The client needs to detect the disconnection event and take appropriate action, such as navigating to a re-login screen or displaying a message like “You have been logged in from another device”.

This effectively allows you to prohibit concurrent play from multiple devices.

Disconnecting all players on a version update

By combining with GS2-Version, when a new version is released you can disconnect all players’ WebSocket sessions, forcing them to pass the version check at reconnection time.

For details, see “Version Update Operating Procedures” in GS2-Version.

Detailed Reference