# 機能設計書: マルチプレイヤー

## 1. 機能概要

### 1.1 機能名
マルチプレイヤー - SceneMultiplayer/RPCによるネットワーク同期

### 1.2 機能説明
SceneMultiplayerは、Godotのマルチプレイヤー機能の中核を担うクラスである。RPC（Remote Procedure Call）による関数呼び出し、オブジェクトの自動スポーン/デスポーン、プロパティの同期レプリケーション、認証システム、サーバーリレーなどの機能を提供する。MultiplayerPeerを通じてENet、WebSocket等の様々なネットワークプロトコルに対応し、リアルタイムマルチプレイヤーゲームの開発基盤として機能する。

### 1.3 関連画面
- なし（スクリプトベースの機能）

## 2. 機能詳細

### 2.1 主要機能一覧

| 機能ID | 機能名 | 説明 |
|--------|--------|------|
| F40-01 | RPC (Remote Procedure Call) | 他ピアでの関数呼び出し |
| F40-02 | オブジェクトスポーン | MultiplayerSpawnerによる同期生成 |
| F40-03 | プロパティ同期 | MultiplayerSynchronizerによる自動同期 |
| F40-04 | 認証システム | カスタム認証コールバック |
| F40-05 | サーバーリレー | クライアント間通信の中継 |
| F40-06 | 帯域幅プロファイリング | デバッグ用帯域幅計測 |
| F40-07 | 転送モード | Reliable/Unreliable/Ordered選択 |
| F40-08 | パスキャッシュ | ノードパスの効率的な送信 |

### 2.2 クラス構造

```
RefCounted
    └── MultiplayerAPI (抽象クラス)
            │
            └── SceneMultiplayer (実装)
                    │
                    ├── SceneCacheInterface (パスキャッシュ)
                    ├── SceneReplicationInterface (同期)
                    └── SceneRPCInterface (RPC処理)

PacketPeer
    └── MultiplayerPeer (抽象クラス)
            │
            ├── ENetMultiplayerPeer
            ├── WebSocketMultiplayerPeer
            └── OfflineMultiplayerPeer

Node
    ├── MultiplayerSpawner (スポーン管理)
    └── MultiplayerSynchronizer (同期管理)
```

### 2.3 SceneMultiplayer主要プロパティ

| プロパティ名 | 型 | デフォルト値 | 説明 |
|-------------|-----|-------------|------|
| root_path | NodePath | "" | マルチプレイヤールートパス |
| auth_callback | Callable | null | 認証コールバック |
| auth_timeout | float | 3.0 | 認証タイムアウト（秒） |
| allow_object_decoding | bool | false | オブジェクトデコード許可 |
| refuse_new_connections | bool | false | 新規接続拒否 |
| server_relay | bool | true | サーバーリレー有効 |
| max_sync_packet_size | int | - | 最大同期パケットサイズ |
| max_delta_packet_size | int | - | 最大デルタパケットサイズ |

### 2.4 SceneMultiplayer主要メソッド

| メソッド名 | 戻り値 | 説明 |
|-----------|--------|------|
| poll() | Error | パケットをポーリング処理 |
| clear() | void | 接続をクリア |
| disconnect_peer(id) | void | 指定ピアを切断 |
| send_bytes(bytes, id, mode, channel) | Error | バイト配列を送信 |
| send_auth(id, data) | Error | 認証データを送信 |
| complete_auth(id) | Error | 認証を完了 |
| get_authenticating_peers() | Array | 認証中ピア一覧取得 |
| get_unique_id() | int | 自身のピアID取得 |
| get_peer_ids() | Array | 接続中ピアID一覧 |
| get_remote_sender_id() | int | 送信元ピアID取得 |

### 2.5 MultiplayerPeer TransferMode列挙型

| 定数 | 値 | 説明 |
|------|-----|------|
| TRANSFER_MODE_UNRELIABLE | 0 | 非信頼性（UDP的） |
| TRANSFER_MODE_UNRELIABLE_ORDERED | 1 | 非信頼性+順序保証 |
| TRANSFER_MODE_RELIABLE | 2 | 信頼性（TCP的） |

### 2.6 MultiplayerAPI RPCMode列挙型

| 定数 | 値 | 説明 |
|------|-----|------|
| RPC_MODE_DISABLED | 0 | RPC無効 |
| RPC_MODE_ANY_PEER | 1 | 任意のピアから呼び出し可 |
| RPC_MODE_AUTHORITY | 2 | 権限者のみ呼び出し可 |

### 2.7 NetworkCommands列挙型

| 定数 | 値 | 説明 |
|------|-----|------|
| NETWORK_COMMAND_REMOTE_CALL | 0 | RPC呼び出し |
| NETWORK_COMMAND_SIMPLIFY_PATH | 1 | パス簡略化 |
| NETWORK_COMMAND_CONFIRM_PATH | 2 | パス確認 |
| NETWORK_COMMAND_RAW | 3 | 生データ送信 |
| NETWORK_COMMAND_SPAWN | 4 | スポーン |
| NETWORK_COMMAND_DESPAWN | 5 | デスポーン |
| NETWORK_COMMAND_SYNC | 6 | 同期 |
| NETWORK_COMMAND_SYS | 7 | システムコマンド |

### 2.8 シグナル一覧

| シグナル名 | 引数 | 説明 |
|-----------|------|------|
| peer_connected | id: int | ピア接続時 |
| peer_disconnected | id: int | ピア切断時 |
| peer_authenticating | id: int | 認証開始時 |
| peer_authentication_failed | id: int | 認証失敗時 |
| peer_packet | id: int, packet: PackedByteArray | 生パケット受信時 |
| connected_to_server | - | サーバー接続完了時 |
| connection_failed | - | 接続失敗時 |
| server_disconnected | - | サーバー切断時 |

## 3. 処理フロー

### 3.1 RPC送信処理フロー

```
[@rpc アノテーション付きメソッド呼び出し]
      │
      ▼
[Node.rpc() / rpc_id() 呼び出し]
      │
      ▼
[SceneRPCInterface::rpcp()] - scene_rpc_interface.cpp: 471-532
      │
      ├─ [RPCConfig取得] - _get_node_config()
      │       │
      │       ├─ ノードRPC設定読み込み
      │       └─ スクリプトRPC設定読み込み
      │
      ├─ [call_local判定]
      │       └─ ローカル呼び出しフラグ設定
      │
      └─ [_send_rpc()] - scene_rpc_interface.cpp: 303-469
              │
              ├─ [パケット構築]
              │       │
              │       ├─ コマンドタイプ (NETWORK_COMMAND_REMOTE_CALL)
              │       ├─ ノードID圧縮 (8/16/32ビット)
              │       ├─ メソッドID圧縮 (8/16ビット)
              │       └─ 引数シリアライズ
              │
              ├─ [転送設定]
              │       ├─ set_transfer_channel()
              │       └─ set_transfer_mode()
              │
              └─ [送信]
                      └─ SceneMultiplayer::send_command()
                              │
                              ├─ [リレー判定]
                              │       └─ server_relay有効時はサーバー経由
                              │
                              └─ multiplayer_peer->put_packet()
```

### 3.2 RPC受信処理フロー

```
[SceneMultiplayer::poll()] - scene_multiplayer.cpp: 65-165
      │
      ▼
[multiplayer_peer->get_packet()]
      │
      ▼
[パケットタイプ判定] - _process_packet()
      │
      ├─ NETWORK_COMMAND_REMOTE_CALL
      │       │
      │       └─ [SceneRPCInterface::process_rpc()]
      │               │
      │               ├─ [パケット解析]
      │               │       ├─ ノードID展開
      │               │       └─ メソッドID展開
      │               │
      │               ├─ [権限チェック] - scene_rpc_interface.cpp: 248-259
      │               │       │
      │               │       ├─ RPC_MODE_DISABLED → 拒否
      │               │       ├─ RPC_MODE_ANY_PEER → 許可
      │               │       └─ RPC_MODE_AUTHORITY → 権限者確認
      │               │
      │               ├─ [引数デシリアライズ]
      │               │       └─ decode_and_decompress_variants()
      │               │
      │               └─ [メソッド呼び出し]
      │                       └─ p_node->callp(config.name, args)
      │
      ├─ NETWORK_COMMAND_SPAWN
      │       └─ replicator->on_spawn_receive()
      │
      ├─ NETWORK_COMMAND_DESPAWN
      │       └─ replicator->on_despawn_receive()
      │
      ├─ NETWORK_COMMAND_SYNC
      │       └─ replicator->on_sync_receive()
      │
      └─ NETWORK_COMMAND_RAW
              └─ _process_raw() → emit_signal("peer_packet")
```

### 3.3 認証処理フロー

```
[新規ピア接続]
      │
      ▼
[_add_peer()] - scene_multiplayer.cpp: 355-364
      │
      ├─ [auth_callback設定あり?]
      │       │
      │       ├─ Yes → pending_peers[id]追加
      │       │         └─ emit_signal("peer_authenticating")
      │       │
      │       └─ No → _admit_peer()で即座に接続許可
      │
      ▼
[認証コールバック呼び出し] - scene_multiplayer.cpp: 94-118
      │
      ├─ [認証データ受信]
      │       └─ SYS_COMMAND_AUTH パケット処理
      │               └─ auth_callback.callp(sender, data)
      │
      ├─ [send_auth(id, data)]
      │       └─ 認証データを相手に送信
      │
      └─ [complete_auth(id)] - scene_multiplayer.cpp: 476-496
              │
              ├─ pending_peers[id].local = true
              │
              └─ [remote完了確認]
                      │
                      └─ 両方完了時 → _admit_peer()
      │
      ▼
[_admit_peer()] - scene_multiplayer.cpp: 366-393
      │
      ├─ [サーバーリレー時] 他ピアへ通知
      │       └─ SYS_COMMAND_ADD_PEER送信
      │
      ├─ connected_peers.insert(id)
      │
      └─ emit_signal("peer_connected")
```

### 3.4 サーバーリレー処理フロー

```
[クライアントA → クライアントB 送信]
      │
      ▼
[send_command()] - scene_multiplayer.cpp: 258-284
      │
      ├─ [リレー条件判定]
      │       ├─ server_relay == true
      │       ├─ get_unique_id() != 1 (サーバーでない)
      │       ├─ p_to != 1 (宛先がサーバーでない)
      │       └─ is_server_relay_supported()
      │
      └─ [リレーパケット構築]
              │
              ├─ NETWORK_COMMAND_SYS
              ├─ SYS_COMMAND_RELAY
              ├─ 宛先ID (4バイト)
              └─ 元パケットデータ
              │
              └─ サーバー(ID=1)へ送信
      │
      ▼
[サーバー側: _process_sys()] - scene_multiplayer.cpp: 299-348
      │
      ├─ SYS_COMMAND_RELAY処理
      │
      ├─ [リレーパケット再構築]
      │       └─ 送信元IDを設定
      │
      └─ [宛先へ転送]
              │
              ├─ peer > 0 → 単一宛先
              └─ peer <= 0 → ブロードキャスト
```

### 3.5 スポーン/同期データフロー

```
[MultiplayerSpawner]
      │
      ▼
[spawn(data)]
      │
      ├─ [シーンインスタンス化]
      │       └─ instantiate_scene() / instantiate_custom()
      │
      └─ [ネットワーク通知]
              └─ replicator->on_spawn()
                      └─ NETWORK_COMMAND_SPAWN送信

[MultiplayerSynchronizer]
      │
      ▼
[プロパティ変更検出]
      │
      ├─ [同期間隔チェック]
      │       └─ update_outbound_sync_time()
      │
      └─ [状態送信]
              │
              ├─ get_state() → 現在のプロパティ値取得
              ├─ get_delta_state() → 変更分のみ取得
              │
              └─ replicator->on_sync_receive()
                      └─ NETWORK_COMMAND_SYNC送信

[受信側]
      │
      ├─ on_spawn_receive() → インスタンス生成
      └─ on_sync_receive() → set_state()でプロパティ適用
```

## 4. コードリーディングガイド

### 4.1 推奨読解順序

1. **データ構造の理解**
   - `scene/main/multiplayer_api.h` (36-80行目): MultiplayerAPI基底クラス
   - `scene/main/multiplayer_peer.h` (39-95行目): MultiplayerPeer基底クラス

2. **エントリーポイント**
   - `scene_multiplayer.cpp` **_bind_methods()** (636-679行目): GDScriptバインディング
   - `scene_multiplayer.cpp` **poll()** (65-165行目): メインループ

3. **コア処理**
   - `scene_rpc_interface.cpp` **rpcp()** (471-532行目): RPC送信
   - `scene_rpc_interface.cpp` **process_rpc()** (155-226行目): RPC受信

### 4.2 重要な処理ポイント

#### パケットコマンド判定（scene_multiplayer.cpp: 217-249行目）
```cpp
void SceneMultiplayer::_process_packet(int p_from, const uint8_t *p_packet, int p_packet_len) {
    uint8_t packet_type = p_packet[0] & CMD_MASK;

    switch (packet_type) {
        case NETWORK_COMMAND_SIMPLIFY_PATH:
            cache->process_simplify_path(p_from, p_packet, p_packet_len);
            break;
        case NETWORK_COMMAND_REMOTE_CALL:
            rpc->process_rpc(p_from, p_packet, p_packet_len);
            break;
        case NETWORK_COMMAND_SPAWN:
            replicator->on_spawn_receive(p_from, p_packet, p_packet_len);
            break;
        // ...
    }
}
```

#### RPCモード判定（scene_rpc_interface.cpp: 248-259行目）
```cpp
bool can_call = false;
switch (config.rpc_mode) {
    case MultiplayerAPI::RPC_MODE_DISABLED:
        can_call = false;
        break;
    case MultiplayerAPI::RPC_MODE_ANY_PEER:
        can_call = true;
        break;
    case MultiplayerAPI::RPC_MODE_AUTHORITY:
        can_call = p_from == p_node->get_multiplayer_authority();
        break;
}
```

#### ノードID圧縮（scene_rpc_interface.cpp: 365-392行目）
```cpp
if (psc_id >= 0 && psc_id <= 255) {
    // 1バイトでエンコード
    node_id_compression = NETWORK_NODE_ID_COMPRESSION_8;
    packet_cache.write[ofs] = static_cast<uint8_t>(psc_id);
} else if (psc_id >= 0 && psc_id <= 65535) {
    // 2バイトでエンコード
    node_id_compression = NETWORK_NODE_ID_COMPRESSION_16;
    encode_uint16(static_cast<uint16_t>(psc_id), &(packet_cache.write[ofs]));
} else {
    // 4バイトでエンコード
    node_id_compression = NETWORK_NODE_ID_COMPRESSION_32;
    encode_uint32(psc_id, &(packet_cache.write[ofs]));
}
```

### 4.3 プログラム呼び出し階層図

```
Node.rpc() / rpc_id()
    │
    └── MultiplayerAPI::rpcp()
            │
            └── SceneMultiplayer::rpcp()  [scene_multiplayer.cpp: 576-578]
                    │
                    └── SceneRPCInterface::rpcp()  [scene_rpc_interface.cpp: 471-532]
                            │
                            ├── _get_node_config()  [RPC設定取得]
                            │       ├── get_node_rpc_config()
                            │       └── get_script_instance()->get_rpc_config()
                            │
                            ├── _send_rpc()  [scene_rpc_interface.cpp: 303-469]
                            │       │
                            │       ├── multiplayer_cache->send_object_cache()  [パスキャッシュ]
                            │       │
                            │       ├── encode_and_compress_variants()  [引数シリアライズ]
                            │       │
                            │       └── multiplayer->send_command()  [送信]
                            │               │
                            │               ├── [リレー処理]
                            │               │       └── relay_buffer構築 → サーバーへ送信
                            │               │
                            │               └── multiplayer_peer->put_packet()
                            │
                            └── [call_local時] p_node->callp()  [ローカル呼び出し]

SceneMultiplayer::poll()  [scene_multiplayer.cpp: 65-165]
    │
    ├── _update_status()  [接続状態更新]
    │
    ├── multiplayer_peer->poll()
    │
    └── [パケット処理ループ]
            │
            ├── multiplayer_peer->get_packet()
            │
            ├── [認証中ピア処理]
            │       └── auth_callback.callp()
            │
            └── _process_packet()  [scene_multiplayer.cpp: 213-249]
                    │
                    ├── NETWORK_COMMAND_REMOTE_CALL
                    │       └── rpc->process_rpc()
                    │               │
                    │               ├── _process_get_node()  [ノード解決]
                    │               └── _process_rpc()  [scene_rpc_interface.cpp: 240-301]
                    │                       │
                    │                       ├── [権限チェック]
                    │                       ├── decode_and_decompress_variants()
                    │                       └── p_node->callp()
                    │
                    ├── NETWORK_COMMAND_SPAWN/DESPAWN
                    │       └── replicator->on_spawn_receive/on_despawn_receive()
                    │
                    ├── NETWORK_COMMAND_SYNC
                    │       └── replicator->on_sync_receive()
                    │
                    └── NETWORK_COMMAND_RAW
                            └── _process_raw() → emit_signal("peer_packet")
```

### 4.4 関連ファイル一覧

| ファイルパス | 種別 | 役割 |
|-------------|------|------|
| scene/main/multiplayer_api.h | ヘッダー | MultiplayerAPI基底クラス |
| scene/main/multiplayer_peer.h | ヘッダー | MultiplayerPeer基底クラス |
| modules/multiplayer/scene_multiplayer.h | ヘッダー | SceneMultiplayer定義 |
| modules/multiplayer/scene_multiplayer.cpp | 実装 | SceneMultiplayer実装 |
| modules/multiplayer/scene_rpc_interface.h | ヘッダー | RPC処理インターフェース |
| modules/multiplayer/scene_rpc_interface.cpp | 実装 | RPC送受信処理 |
| modules/multiplayer/scene_cache_interface.h | ヘッダー | パスキャッシュ |
| modules/multiplayer/scene_replication_interface.h | ヘッダー | レプリケーション |
| modules/multiplayer/multiplayer_spawner.h | ヘッダー | スポーナー定義 |
| modules/multiplayer/multiplayer_synchronizer.h | ヘッダー | シンクロナイザー定義 |

## 5. 設計上の考慮事項

### 5.1 ネットワーク効率
- ノードID/メソッドIDの圧縮エンコーディング
- パスキャッシュによるパス文字列の省略
- デルタ同期による帯域幅節約

### 5.2 セキュリティ
- カスタム認証コールバック機構
- RPCモードによるアクセス制御
- オブジェクトデコードのオプトイン

### 5.3 柔軟性
- MultiplayerPeerによるプロトコル抽象化
- MultiplayerAPIExtensionによる拡張可能性
- 可視性フィルターによる同期制御

### 5.4 信頼性
- 転送モード選択（Reliable/Unreliable）
- サーバーリレーによるNAT越え支援
- 認証タイムアウトによるリソース保護

## 6. 使用例

### 6.1 基本的なRPC使用
```gdscript
# サーバー側/クライアント側で定義
@rpc("any_peer", "reliable")
func update_position(pos: Vector2):
    position = pos

# 呼び出し側
func _physics_process(delta):
    if is_multiplayer_authority():
        position += velocity * delta
        update_position.rpc(position)  # 全ピアに送信
```

### 6.2 RPCモード指定
```gdscript
# 誰でも呼び出し可能、信頼性あり
@rpc("any_peer", "reliable")
func chat_message(msg: String):
    add_message(msg)

# 権限者のみ呼び出し可能、ローカル呼び出しも行う
@rpc("authority", "call_local", "reliable")
func apply_damage(amount: int):
    health -= amount
```

### 6.3 マルチプレイヤーサーバー作成
```gdscript
var peer = ENetMultiplayerPeer.new()

func _ready():
    peer.create_server(7777)
    multiplayer.multiplayer_peer = peer
    multiplayer.peer_connected.connect(_on_peer_connected)

func _on_peer_connected(id):
    print("Player connected: ", id)
```

### 6.4 マルチプレイヤークライアント作成
```gdscript
var peer = ENetMultiplayerPeer.new()

func _ready():
    peer.create_client("127.0.0.1", 7777)
    multiplayer.multiplayer_peer = peer
    multiplayer.connected_to_server.connect(_on_connected)

func _on_connected():
    print("Connected to server!")
```

### 6.5 カスタム認証
```gdscript
func _ready():
    multiplayer.auth_callback = _on_auth_receive
    multiplayer.peer_authenticating.connect(_on_peer_authenticating)

func _on_peer_authenticating(id):
    # 認証データ要求
    multiplayer.send_auth(id, "send_credentials".to_utf8_buffer())

func _on_auth_receive(id, data):
    var msg = data.get_string_from_utf8()
    if verify_credentials(msg):
        multiplayer.complete_auth(id)
```

### 6.6 MultiplayerSpawner使用
```gdscript
@onready var spawner = $MultiplayerSpawner

func _ready():
    spawner.spawn_path = NodePath("../Players")
    spawner.add_spawnable_scene("res://player.tscn")

func spawn_player():
    if multiplayer.is_server():
        spawner.spawn()  # 全クライアントで自動スポーン
```

### 6.7 MultiplayerSynchronizer使用
```gdscript
# シーン内にMultiplayerSynchronizerを配置
# SceneReplicationConfigで同期プロパティを設定

func _ready():
    $MultiplayerSynchronizer.set_multiplayer_authority(multiplayer.get_unique_id())

# positionプロパティが自動的に全ピアに同期される
func _physics_process(delta):
    if is_multiplayer_authority():
        position += velocity * delta
```

### 6.8 生データ送信
```gdscript
func send_custom_data(data: PackedByteArray, target: int = 0):
    multiplayer.send_bytes(data, target, MultiplayerPeer.TRANSFER_MODE_RELIABLE)

func _ready():
    multiplayer.peer_packet.connect(_on_peer_packet)

func _on_peer_packet(id, packet):
    print("Received custom packet from ", id)
```

## 7. 関連機能
- [No.38 HTTPリクエスト](./38-HTTPリクエスト.md) - HTTP通信
- [No.39 WebSocket](./39-WebSocket.md) - WebSocket通信
