Newer
Older
PixelPaintWar / docs / 05_TECH / TECH_01_通信最適化.txt
========================================================================
通信最適化 (Network Optimization)
========================================================================


1. 概要 (Overview)
------------------------------------------------------------------------

1-1. 目的
    最大100人が同時接続するリアルタイム対戦ゲームにおいて,サーバー・クライアント間の通信帯域と
    メッセージ数を最小化しつつ,低遅延な状態同期を実現する.

1-2. 主要な最適化手法
    ・AOI(関心領域)フィルタリング: プレイヤーの周辺データのみ送信する
    ・ティックバッチ送信: 50ms間隔で差分をまとめて1回送信する
    ・座標量子化: 浮動小数点座標を丸めて帯域を削減する
    ・差分同期: 前回送信値から変化した分のみ送信する
    ・マップセルのグループ化: セル更新をチームIDでまとめて送信する


2. AOI(関心領域)フィルタリング (Area of Interest)
------------------------------------------------------------------------

2-1. 仕組み
    フィールド全体をAOIセル(3×3グリッド単位)に分割し,各プレイヤーの周辺ウィンドウ
    (横5×縦3 AOIセル = 15×9グリッド)に含まれるデータのみを送信する.

2-2. AOIグリッド設定
    ・AOIセルサイズ: 3グリッド単位
    ・AOIウィンドウ: 横5セル × 縦3セル

2-3. フィルタリング対象
    ・プレイヤー位置更新(UPDATE_PLAYERS)
    ・ボム設置通知(BOMB_PLACED)
    ・ハリケーン更新(UPDATE_HURRICANES)

2-4. AOIセルキャッシュ
    ・各ソケットごとに前回のAOIセル座標を保持する
    ・プレイヤーのAOIセルが変化した場合のみウィンドウ再計算を行う
    ・同一セル内の移動ではフィルタリング処理をスキップする

2-5. プレイヤー可視性管理
    各ソケットごとに以下のSetを保持し,AOI境界の出入りを検知する.
    ・visiblePlayerIds: 現在可視のプレイヤーIDセット
    ・visibleBombIds: 現在可視のボムIDセット
    ・visibleHurricaneIds: 現在可視のハリケーンIDセット

    ■ 出入り検知
    ・前回不可視 → 今回可視: NEW_PLAYERイベントを発行する
    ・前回可視 → 今回不可視: REMOVE_PLAYERイベントを発行する
    ・継続可視のプレイヤーのみUPDATE_PLAYERSに含める


3. 20Hzティック&バッチ送信 (Tick-Based Batching)
------------------------------------------------------------------------

3-1. ティックレート
    ・サーバーゲームループ: 20Hz(50ms間隔)
    ・スケジュール方式: setTimeoutで次回ティック時刻を正確に計算し,setIntervalのドリフトを回避する

3-2. バッチ処理
    1ティックで以下の処理をまとめて実行し,結果を1回で送信する.
    1. ハリケーンの位置更新と衝突判定
    2. Bot AIの行動決定と位置更新
    3. マップの塗り処理と競合判定
    4. ボムの爆発判定(Bot向け)
    5. 差分データの組み立て

3-3. 送信メッセージ(1ティックあたり最大)
    ・UPDATE_PLAYERS: 位置が変化したプレイヤーのみ
    ・UPDATE_MAP_CELLS: 塗り状態が変化したセルのみ
    ・UPDATE_HURRICANES: 状態が変化したハリケーンのみ
    ・変化がない種別のメッセージは送信しない


4. 座標量子化 (Position Quantization)
------------------------------------------------------------------------

4-1. プレイヤー座標
    ・量子化スケール: 100(小数第2位で丸める)
    ・計算式: Math.round(value × 100) / 100
    ・例: 3.14159 → 3.14

4-2. ハリケーン座標
    ・位置の量子化スケール: 10(小数第1位で丸める)
    ・回転の量子化スケール: 4(0.25刻み)
    ・プレイヤーより粗い精度で十分なため,帯域をさらに削減する

4-3. 3層の量子化パイプライン(プレイヤー位置)
    1. クライアント送信時: 量子化後の座標が前回と同じなら送信をスキップする
    2. サーバー受信時: 受信座標を再量子化して精度を統一する
    3. サーバー送信時: 量子化後の座標で差分比較し,変化分のみ送信する

4-4. 異常値ガード
    ・NaN/Infinityの場合は0を返す
    ・量子化スケールが無効(0以下)の場合は元の値をそのまま返す


5. 差分同期 (Delta Synchronization)
------------------------------------------------------------------------

5-1. 汎用デルタ抽出ユーティリティ
    ・collectSyncDeltaEntries(): IDベースのスナップショット比較を行う汎用関数
    ・前回送信したスナップショットをMapで保持し,変化したエントリのみを返す
    ・プレイヤー位置同期とハリケーン同期の両方で共通利用する

5-2. プレイヤー位置の差分
    ・ソケットごとにlastSentPositionByPlayerIdを保持する
    ・量子化後の座標が前回と同じプレイヤーはUPDATE_PLAYERSから除外する
    ・切断プレイヤーのキャッシュは自動クリーンアップする

5-3. UPDATE_PLAYERSのteamId省略
    ・初回接続時のCURRENT_PLAYERSでteamIdを含む完全なPlayerDataを送信する
    ・以降のUPDATE_PLAYERSではid, x, yのみ送信し,teamIdを省略する
    ・毎ティックのペイロードサイズを削減する


6. マップセルのグループ化 (Grouped Cell Updates)
------------------------------------------------------------------------

6-1. データ形式
    セル更新を個別のオブジェクト配列ではなく,チームIDをキーとしたグループ形式で送信する.

    ■ 変換前(個別形式)
    [{ index: 5, teamId: 2 }, { index: 7, teamId: 2 }, { index: 10, teamId: 1 }]

    ■ 変換後(グループ形式: GroupedCellUpdates)
    { "2": [5, 7], "1": [10] }

6-2. 効果
    ・teamIdキーの重複を排除し,ペイロードサイズを削減する
    ・セル数が多いほど圧縮効率が高くなる

6-3. 差分のみ送信
    ・MapStoreのpendingUpdatesキューに変化セルのみを蓄積する
    ・既に同じチームで塗られているセルはキューに追加しない
    ・getAndClearUpdates()でキューの参照をゼロコピーで差し替える


7. クライアント側の送信最適化 (Client-Side Send Optimization)
------------------------------------------------------------------------

7-1. 位置送信の間引き
    ・PlayerMoveSenderが量子化後の座標を前回送信値と比較する
    ・変化がない場合は送信をスキップする
    ・停止時にはforce: trueオプションで最終座標を確実に送信する

7-2. 送信間隔
    ・PLAYER_POSITION_UPDATE_MS: 50ms間隔でゲームループから呼び出される
    ・ティックレートと同期し,サーバーが処理可能な頻度に制限する


8. パフォーマンス計測 (Performance Monitoring)
------------------------------------------------------------------------

8-1. 計測項目(1秒ウィンドウ単位)
    ・tickCount: ウィンドウ内のティック数
    ・avgTickMs: ティックあたりの平均処理時間
    ・maxTickMs: ピーク処理時間
    ・cpuUsagePct: ゲームロジックのCPU使用率(= totalTickMs / windowMs × 100)
    ・avgPayloadBytesPerTick: ティックあたりの平均ペイロードサイズ
    ・outboundBytesPerSec: 推定送信帯域(= avgPayload × playerCount × tickCount)

8-2. 活用
    ・1秒ごとにログ出力し,ボトルネックの検知に利用する
    ・ティック処理が50msを超えた場合はキャッチアップ機構が作動する