# Tutorial 6 - Group

このチュートリアルでは、Diarkis の **Group** 機能を実装します。Group は文字列 ID で識別される軽量な pub/sub チャンネルです。同じ ID を知っているクライアントが全員同じチャンネルに参加してブロードキャストし合えます。

終了時には以下のことが身についています:

* `Group.SendJoin()` で Group に参加する（存在しなければ自動作成）方法
* `OnGroupCreate`（新規作成）と `OnGroupJoin`（既存参加）を区別する方法
* `Group.SendBroadcast()` で全メンバーにブロードキャストする方法
* `OnGroupMemberBroadcast` でブロードキャストを受信する方法
* `Group.SendLeave()` で退出する方法

Tutorial 1 の接続フローが前提です。このシーンは `Start()` で自動接続します。

### Group と Room の違い

| 項目          | Room            | Group                          |
| ----------- | --------------- | ------------------------------ |
| 識別子         | サーバーが生成する ID    | 任意の文字列（例: `"global-chat"`）     |
| 作成          | 明示的な作成またはランダム参加 | `SendJoin` 時に存在しなければ自動作成       |
| サーバー側メンバー管理 | あり（最大メンバー数、TTL） | なし（軽量）                         |
| 複数同時参加      | 1 つ             | 複数の Group に同時参加可能              |
| 用途例         | ゲームセッション、マッチ    | グローバルチャット、チームチャンネル、話題別 pub/sub |

Group ID は自分で決める文字列なので、同じ ID を知っているクライアントが自動的に同じチャンネルを共有します。事前の発見ステップは不要です。

```mermaid
flowchart LR
    A[クライアント A] -->|SendJoin global-chat| SRV[(Diarkis サーバー)]
    B[クライアント B] -->|SendJoin global-chat| SRV
    C[クライアント C] -->|SendJoin team-red| SRV
    SRV -->|Broadcast global-chat| A
    SRV -->|Broadcast global-chat| B
    SRV -.->|届かない| C
```

### シーンのセットアップ

`Tutorials/Scenes/Tutorial6-Group.unity` を開き、定数を環境に合わせて変更してください。

```csharp
private const string HOST       = "127.0.0.1:7000";
private const string CLIENT_KEY = "";
private const string UID        = ""; // 空文字の場合はランダム生成
```

Play モードに入るとシーンが自動接続します。2 つのクライアントで同じ Group ID（例: `global-chat`）を入力して **Join** を押してください。メッセージボタンを押すと相手のチャットログに表示されます。

### コードの解説

#### イベントの登録

```csharp
DiarkisEventHandler handler = diarkis.EventHandler;

handler.OnUDPConnect(OnConnect, this);
handler.OnUDPDisconnect(OnDisconnect, this);
handler.OnUDPFail(_ => SetNetworkState("接続失敗", ColorRed), this);
handler.OnHttpError(_ => SetNetworkState("HTTP 認証エラー", ColorRed), this);

handler.OnGroupCreate(OnGroupCreated, this);
handler.OnGroupJoin(OnGroupJoined, this);
handler.OnGroupLeave(OnGroupLeft, this);
handler.OnGroupMemberJoin(_ => AddChatLine("[システム] メンバーが参加しました"), this);
handler.OnGroupMemberLeave(_ => AddChatLine("[システム] メンバーが退出しました"), this);
handler.OnGroupMemberBroadcast(OnBroadcastReceived, this);
```

このチュートリアルで登録するイベントの一覧です。

| イベント                     | 発火タイミング                              | コールバックシグネチャ                           |
| ------------------------ | ------------------------------------ | ------------------------------------- |
| `OnGroupCreate`          | `SendJoin` で新規 Group が作成された（最初のメンバー） | `Action<DiarkisGroupEventArgs>`       |
| `OnGroupJoin`            | `SendJoin` で既存の Group に参加した          | `Action<DiarkisGroupEventArgs>`       |
| `OnGroupLeave`           | 自分が Group から退出した                     | `Action<DiarkisGroupEventArgs>`       |
| `OnGroupMemberJoin`      | 他のメンバーが参加した                          | `Action<DiarkisGroupMemberEventArgs>` |
| `OnGroupMemberLeave`     | メンバーが退出した                            | `Action<DiarkisGroupMemberEventArgs>` |
| `OnGroupMemberBroadcast` | 他メンバーからブロードキャストが届いた                  | `Action<DiarkisPayloadEventArgs>`     |

#### SendJoin()

```csharp
private void OnJoinClicked()
{
    string groupID = _groupIDInput.text.Trim();
    if (string.IsNullOrEmpty(groupID)) return;

    DiarkisNetworkManager.GetDiarkisInterface(INTERFACE_NAME).Group.SendJoin(groupID);
}
```

Group が存在しなければ自動作成します。結果は `OnGroupCreate` または `OnGroupJoin` で届きます。

#### OnGroupCreate と OnGroupJoin

両方とも同じシグネチャと同じデータアクセスパターンです。

```csharp
private void OnGroupCreated(DiarkisGroupEventArgs e)
{
    if (!e.IsSuccess()) { AddChatLine($"[エラー] {e.GetErrorMessage()}"); return; }
    _currentGroupID = e.GetGroupID();
    _inGroup        = true;
    AddChatLine($"[システム] グループを作成しました: {_currentGroupID}");
    RefreshButtons();
}

private void OnGroupJoined(DiarkisGroupEventArgs e)
{
    if (!e.IsSuccess()) { AddChatLine($"[エラー] {e.GetErrorMessage()}"); return; }
    _currentGroupID = e.GetGroupID();
    _inGroup        = true;
    AddChatLine($"[システム] グループに参加しました: {_currentGroupID}");
    RefreshButtons();
}
```

`e.GetGroupID()` で参加した Group の ID を確認します。

#### SendBroadcast()

```csharp
private void OnMessageClicked(string message)
{
    if (!_inGroup) return;

    byte[] bytes = Encoding.UTF8.GetBytes(message);
    DiarkisNetworkManager.GetDiarkisInterface(INTERFACE_NAME).Group.SendBroadcast(_currentGroupID, bytes);

    // ブロードキャストは送信者自身には届かないため、ローカルに表示する
    AddChatLine($"[自分] {message}");
}
```

`Room.SendBroadcastToRoom` と同様に、Group のブロードキャストも**送信者自身には届きません**。ローカルに直接表示します。

#### ブロードキャストの受信

```csharp
private void OnBroadcastReceived(DiarkisPayloadEventArgs e)
{
    DiarkisByteVector payload = e.GetPayload();
    if (payload == null || payload.Count == 0) return;

    byte[] bytes = new byte[payload.Count];
    for (int i = 0; i < payload.Count; i++)
        bytes[i] = payload[i];

    string message = Encoding.UTF8.GetString(bytes);
    AddChatLine($"[受信] {message}");
}
```

`DiarkisByteVector` → `byte[]` の変換パターンは Tutorial 4・5 と同じです。

#### 参加・送受信フロー

```mermaid
flowchart TD
    J["SendJoin(groupID)"] -->|新規作成| C["OnGroupCreate\ne.GetGroupID()"]
    J -->|既存参加| V["OnGroupJoin\ne.GetGroupID()"]
    C --> B["SendBroadcast(groupID, bytes)"]
    V --> B
    B -->|送信者以外の全メンバー| R["OnGroupMemberBroadcast\ne.GetPayload()"]
    L["SendLeave(groupID)"] --> GL["OnGroupLeave"]
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.diarkis.io/diarkis-client/chtoriaru/unity-chtoriaru/tutorial-6-group.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
