> For the complete documentation index, see [llms.txt](/llms.txt)

# GS2-Chat

テキストチャット機能




GS2-Chat を利用することで、ゲーム内にテキストチャットを組み込むことが可能です。
テキストチャットは新着メッセージの検知のために常時接続セッションを必要とし、サーバーに求める要件も複雑になります。

Game Server Services を利用すれば、そのようなめんどくさい常時接続セッションの管理からも解放されます。

## ルーム

GS2-Chat の利用を開始するには、まずはルームを作成する必要があります。
ルームはプレイヤーが自由に作成できるようにもできますし、開発サイドで用意したルームをプレイヤーに使用させることもできます。
また、投稿できるユーザーIDのホワイトリストを設定して特定メンバー専用のルームを構築することも可能です。

一点気をつけなければならないのが、GS2-Chat は投稿可能なメッセージの数をルームごとに1秒間あたり3件までしか保証しません。
これを超える頻度でメッセージの投稿を行うとエラーが発生する可能性があります。
そのため、ソーシャルネットワークのタイムラインのような大量のプレイヤーを1つのルームに格納するような要件には向きません。

### パスワード

ルームにはパスワードを設定することが可能です。
パスワードが設定されたルームのメッセージを取得する際や、メッセージを投稿する際にはパスワードの指定が必要となります。

### ルーム設定の更新

ルームの作成後、メタデータやパスワード、ホワイトリスト（投稿可能なユーザーIDリスト）を更新することが可能です。これにより、動的にルームのアクセス権限を変更したり、ルームの属性情報を変更したりできます。

## 購読

プレイヤーはルームを購読することで、ルームへの新着メッセージの通知を受けることができるようになります。
メッセージにはカテゴリを設定可能で、ルーム内のカテゴリごとに購読が可能です。

ギルドチャットの実装においてギルドマスターからのメッセージだけカテゴリを特別なものに設定し、ギルドメンバーはギルドマスターからのメッセージだけは絶対に購読させるようなことができます。

購読設定は後から更新することも可能です。受信するカテゴリの追加・削除や、オフライン時のモバイルプッシュ通知転送設定を動的に変更できます。

購読による通知処理は GS2-Gateway の通知機能が利用されますが、GS2-Gateway には通知先プレイヤーがオフラインの場合
モバイルプッシュ通知などの、ゲーム外の方法で通知する機能が用意されています。

1つのルームを購読できる人数に明確な制限はありませんが、通知を送信するために GS2-Gateway のAPIリクエストが発生するため、GS2のAPI利用料金が通知先1件ごとに発生します。
また、通知先が多くなると通知が届くまでにかかる時間が長くなります。

ルーム名を指定して、そのルームを現在誰が購読しているかの一覧を取得することも可能です。

```plantuml
actor Player1
actor Player2
participant "GS2-Chat#Room"
participant "GS2-Gateway#Namespace"
participant "Firebase Cloud Messaging"
Player1 -> "GS2-Chat#Room" : Subscribe
Player2 -> "GS2-Chat#Room" : Post Message
"GS2-Chat#Room" -> "GS2-Gateway#Namespace" : Send Notification to Player1
"GS2-Gateway#Namespace" -> "Firebase Cloud Messaging" : Notification(if player is offline)
"GS2-Gateway#Namespace" -> Player1 : Notification
Player1 -> "GS2-Chat#Room" : Load New Message
```

## メッセージ

ルームに対してメッセージを送信することが可能です。
メッセージの保持期間はネームスペースの設定 `messageLifeTimeDays` で1〜30日の範囲で指定でき、保持期間を超えたメッセージは削除されます。

また、最新のメッセージを一定件数取得する機能も用意されています。これにより、ルームに入室した直後の過去ログ表示などを効率的に行うことができます。

## NGワード

メッセージのペイロードに不適切なワードが含まれるかもしれません。
GS2-Script と連携することで、不適切なワードが含まれるメッセージの投稿を拒否したり、メッセージのペイロードを書き換えることができます。

GS2-Script は HTTP 通信を発行することができるため、自社でもつNGワードチェックサーバーにフォワードして処理することも可能です。

```plantuml
actor Player
participant "GS2-Chat#Room"
participant "GS2-Script#Script"
Player -> "GS2-Chat#Room" : Post Message
"GS2-Chat#Room" -> "GS2-Script#Script" : Trigger post message
"GS2-Script#Script" -> "GS2-Chat#Room" : Continue? / Replaced Message
```

## カテゴリーモデルと通知設定

メッセージのカテゴリはマスターデータで定義でき、カテゴリごとにアクセストークンを用いた投稿を拒否するかや、オフライン時にモバイルプッシュ通知へ転送するかを設定できます。

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

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

メッセージ投稿のスクリプトトリガーから他マイクロサービスのAPIを呼び出すことで、特定のメッセージ投稿に応じてユーザーデータを書き換えるといった連携は可能です。

## スクリプトトリガー

ルームの作成・削除、メッセージ投稿、購読や購読解除の各処理の前後で GS2-Script を呼び出せます。同期実行で検証や改ざんを行ったり、非同期実行で Amazon EventBridge 経由の外部連携を組み込むこともできます。

設定できる主なイベントトリガーとスクリプト設定名は以下の通りです。

- `createRoomScript`（完了通知: `createRoomDone`）: ルーム作成の前後
- `deleteRoomScript`（完了通知: `deleteRoomDone`）: ルーム削除の前後
- `postMessageScript`（完了通知: `postMessageDone`）: メッセージ投稿の前後
- `subscribeRoomScript`（完了通知: `subscribeRoomDone`）: ルーム購読の前後
- `unsubscribeRoomScript`（完了通知: `unsubscribeRoomDone`）: 購読解除の前後

## プッシュ通知

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

- `postNotification`: 購読しているルームに新しい投稿があったときに通知

通知カテゴリは `notificationTypes` で制御でき、オフライン端末へのモバイルプッシュ転送設定も可能です。

## 実装例

### ルームの作成



**Unity**
```csharp

    var result = await gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).CreateRoomAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    );
    const auto Future = Domain->CreateRoom(
        "room-0001", // name
        nullptr, // metadata
        nullptr, // password
        nullptr // whiteListUserIds
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();
```


### ルームの購読



**Unity**
```csharp

    var result = await gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    ).SubscribeAsync(
        notificationTypes: new [] {
            new Gs2.Unity.Gs2Chat.Model.EzNotificationType {
                category = 0,
                enableTransferMobilePushNotification = false,
            },
        }
    );
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Subscribe(
        "room-0001" // roomName
    );
    const auto Future = Domain->Subscribe(
        []
        {
            const auto v = MakeShared<TArray<TSharedPtr<Gs2::Chat::Model::FNotificationType>>>();
            v->Add(MakeShared<Gs2::Chat::Model::FNotificationType>());
            return v;
        }() // notificationTypes
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();
```


### ルームの購読解除



**Unity**
```csharp

    var result = await gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Subscribe(
        roomName: "room-0001"
    ).UnsubscribeAsync(
    );
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Subscribe(
        "room-0001" // roomName
    );
    const auto Future = Domain->Unsubscribe(
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;
```


### メッセージの投稿



**Unity**
```csharp

    var result = await gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    ).PostAsync(
        metadata: "MESSAGE_0001",
        category: 0
    );
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto Future = Domain->Post(
        "MESSAGE_0001", // metadata
        0 // category
    );
    Future->StartSynchronousTask();
    if (Future->GetTask().IsError()) return false;

    // obtain changed values / result values
    const auto Future2 = Future->GetTask().Result()->Model();
    Future2->StartSynchronousTask();
    if (Future2->GetTask().IsError()) return false;
    const auto Result = Future2->GetTask().Result();
```


### メッセージの一覧取得



**Unity**
```csharp

    var items = await gs2.Chat.Namespace(
        namespaceName: "namespace-0001"
    ).Me(
        gameSession: GameSession
    ).Room(
        roomName: "room-0001",
        password: null
    ).MessagesAsync(
    ).ToListAsync();
```
**Unreal Engine**
```cpp

    const auto Domain = Gs2->Chat->Namespace(
        "namespace-0001" // namespaceName
    )->Me(
        AccessToken
    )->Room(
        "room-0001", // roomName
        nullptr // password
    );
    const auto It = Domain->Messages(
    );
    TArray<Gs2::UE5::Chat::Model::FEzMessagePtr> Result;
    for (auto Item : *It)
    {
        if (Item.IsError())
        {
            return false;
        }
        Result.Add(Item.Current());
    }
```


### メッセージの受信通知ハンドリング

MessagesAsync はキャッシュがある場合はキャッシュを応答し、メッセージの一覧で取得できるデータのキャッシュはメッセージ受信通知を受け取った際に、SDKによって自動的に更新されます。
そのため、通常はこのイベントをハンドリングする必要はなく、必要なタイミングで MessagesAsync を呼び出せば、通常はキャッシュを利用して最新のメッセージリストにアクセスできます。



**Unity**
```csharp

    gs2.Chat.OnPostNotification += PostNotification =>
    {
        var namespaceName = PostNotification.NamespaceName;
        var roomName = PostNotification.RoomName;
    };
```
**Unreal Engine**
```cpp

    Gs2->Chat->OnPostNotification().AddLambda([](const auto Notification)
    {
        const auto NamespaceName = Notification->NamespaceNameValue;
        const auto RoomName = Notification->RoomNameValue;
    });
```


## その他の機能

### クライアントから投稿できないカテゴリ

マスターデータの CategoryModel で rejectAccessTokenPost に true を設定することで、該当カテゴリへアクセストークンを使用して投稿できなくなります。
この機能を活用することで、スクリプトなどを経由してしか投稿できないカテゴリを用意し、システムメッセージを格納するような用途に利用できます。

### プレイヤーによるルーム作成の可否

ネームスペース設定の allowCreateRoom を利用すると、プレイヤーが自由にルームを作成できるかどうかを管理側で制御できます。

allowCreateRoom=true : プレイヤーが自分でルームを作成可能
allowCreateRoom=false : 運営が用意したルームのみ利用可能

大規模なルーム乱立を防ぎたい場合や、特定のルームのみを許可したい場合に、この設定を適宜調整してください。

## メッセージカテゴリの活用例

メッセージにはカテゴリを設定できます。カテゴリを使うことで、クライアント側でペイロードのパース方法を切り替えることができます。

たとえば以下のような使い方ができます。

- `カテゴリ0`: テキストがペイロードにそのまま含まれる通常のテキストメッセージ
- `カテゴリ1`: ペイロードにスタンプのIDが含まれるスタンプメッセージ

このようにカテゴリをメッセージの種類や重要度別に分けることで、ゲームプレイヤーが受け取る通知の種類を自分で設定できるようになり、ユーザー体験を向上させることができます。

### マスターデータ管理
マスターデータを登録することでマイクロサービスで利用可能なデータや振る舞いを設定できます。

マスターデータの種類には以下があります。

- `CategoryModel`: カテゴリ番号ごとの投稿可否（`rejectAccessTokenPost`）、およびオフライン時のモバイルプッシュ通知転送設定

マスターデータの登録はマネージメントコンソールから登録する他、GitHubからデータを反映したり、GS2-Deployを使ってCIから登録するようなワークフローを組むことが可能です。

## 詳細なリファレンス

[GS2-Chat API リファレンス](../../api_reference/chat)



