# 通知設計書 68-NOTIFICATION_EDITOR_PRE_SAVE

## 概要

本ドキュメントは、Godotエンジンにおける`NOTIFICATION_EDITOR_PRE_SAVE`通知の設計仕様を定義する。この通知はGodotエディタでシーンが保存される直前に、シーン内のすべてのノードに対して送信される内部通知である。

### 本通知の処理概要

`NOTIFICATION_EDITOR_PRE_SAVE`通知は、エディタがシーンをディスクに保存する直前のタイミングで、対象シーンのすべてのノードに対して送信される。これにより、各ノードは保存前に必要な準備処理や状態の調整を行うことができる。

**業務上の目的・背景**：シーン保存時に、エディタ専用のデータを一時的に除外したり、保存用に状態を正規化したり、一時的な変更を取り消したりする必要がある場合がある。例えば、Skeleton3Dはアニメーションプレビュー中のポーズを保存しないようにするためにこの通知を利用する。

**通知の送信タイミング**：EditorNode::_save_scene()メソッド内で、実際のシーンパック処理（PackedScene::pack()）が実行される直前にpropagate_notificationで送信される。

**通知の受信者**：保存対象シーンのルートノードおよびその全子孫ノード。_notificationメソッドで受信して処理する。

**通知内容の概要**：通知値は`9001`（Node::NOTIFICATION_EDITOR_PRE_SAVE）。追加のパラメータは含まれない。

**期待されるアクション**：受信者は保存前の状態調整（エディタ専用データの除外、一時状態のクリア、savingフラグの設定など）を実装する。

## 通知種別

アプリ内通知（エンジン内部通知・エディタ専用）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期 |
| 優先度 | 高 |
| リトライ | なし |

### 送信先決定ロジック

シーンのルートノードに対してpropagate_notificationで送信され、全子孫ノードに伝播される。

## 通知テンプレート

### エンジン内部通知の場合

| 項目 | 内容 |
|-----|------|
| 通知定数名 | NOTIFICATION_EDITOR_PRE_SAVE |
| 通知値 | 9001 |
| 定義クラス | Node |

### 通知ペイロード

通知に追加データは含まれない。

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| p_notification | 通知ID（9001） | エンジン内部 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ユーザー操作 | Ctrl+S / File > Save Scene | シーン保存要求 | ユーザーがシーン保存を実行した時 |
| API呼び出し | EditorNode::_save_scene() | プログラムからの保存 | スクリプトからの保存呼び出し時 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| シーンなし | 保存対象のシーンルートがnullの場合 |
| エディタ外 | ゲーム実行時（エディタ外）では送信されない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[シーン保存要求] --> B[EditorNode::_save_scene]
    B --> C{シーンルート存在?}
    C -->|Yes| D[scene->propagate_notification PRE_SAVE]
    C -->|No| E[エラー表示して終了]
    D --> F[各ノードの_notification受信]
    F --> G[保存前準備処理]
    G --> H[editor_data.apply_changes_in_editors]
    H --> I[PackedScene::pack]
    I --> J[ResourceSaver::save]
    J --> K[NOTIFICATION_EDITOR_POST_SAVE送信]
    K --> L[処理終了]
    E --> L
```

## データベース参照・更新仕様

### 参照テーブル一覧

本通知はデータベースを使用しない。

### 更新テーブル一覧

本通知はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| シーンなし | 保存対象のシーンが存在しない | エラーダイアログを表示 |
| 循環参照 | シーンに循環インスタンスが含まれる | 保存を中止しエラー表示 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（ユーザー操作で再試行） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（ユーザー操作依存） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

エディタ使用時のみ

## セキュリティ考慮事項

- 本通知はエディタ内部の通知であり、ゲーム実行時には発生しない
- TOOLS_ENABLED（エディタビルド）でのみコンパイルされる

## 備考

- 対となる通知として`NOTIFICATION_EDITOR_POST_SAVE`（9002）がある
- Skeleton3Dでは`saving = true`フラグを設定し、アニメーションポーズを保存から除外
- Controlノードではエディタ固有の状態リセットに使用される場合がある

---

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

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、通知定数の定義場所を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | node.h | `scene/main/node.h` | 501行目でNOTIFICATION_EDITOR_PRE_SAVEが9001として定義されていることを確認 |

**読解のコツ**: エディタ専用通知は9000番台で定義されている点に注目する。

#### Step 2: エントリーポイントを理解する

通知を発火するEditorNode::_save_sceneメソッドを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | editor_node.cpp | `editor/editor_node.cpp` | 2414行目で`scene->propagate_notification(NOTIFICATION_EDITOR_PRE_SAVE);`が呼び出される |

**主要処理フロー**:
1. **2400行目**: _save_scene()メソッド開始
2. **2402行目**: シーンルートを取得
3. **2414行目**: `scene->propagate_notification(NOTIFICATION_EDITOR_PRE_SAVE);`で通知送信
4. **2416行目**: editor_data.apply_changes_in_editors()
5. **2438行目**: PackedScene::pack()でシーンをパック
6. **2451行目**: ResourceSaver::save()で保存実行
7. **2491行目**: `scene->propagate_notification(NOTIFICATION_EDITOR_POST_SAVE);`

#### Step 3: 受信側の処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | skeleton_3d.cpp | `scene/3d/skeleton_3d.cpp` | 304-306行目でNOTIFICATION_EDITOR_PRE_SAVEを処理してsaving=trueを設定 |
| 3-2 | control.cpp | `scene/gui/control.cpp` | エディタ保存時の状態処理 |

**主要処理フロー**:
- **304行目**: `case NOTIFICATION_EDITOR_PRE_SAVE:`
- **305行目**: `saving = true;`

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

```
EditorNode
    │
    └─ _save_scene(p_file, idx)
           │
           ├─ scene->propagate_notification(NOTIFICATION_EDITOR_PRE_SAVE)
           │      │
           │      └─ 各ノードの_notification()
           │             ├─ Skeleton3D: saving = true
           │             └─ その他ノード: 保存前処理
           │
           ├─ editor_data.apply_changes_in_editors()
           │
           ├─ PackedScene::pack(scene)
           │
           ├─ ResourceSaver::save()
           │
           └─ scene->propagate_notification(NOTIFICATION_EDITOR_POST_SAVE)
```

### データフロー図

```
[入力]                    [処理]                              [出力]

シーン保存       ───▶ EditorNode::_save_scene()    ───▶ PRE_SAVE通知
要求                          │                               │
                              ▼                               ▼
                    propagate_notification        各ノード保存前処理
                              │                               │
                              ▼                               ▼
                    PackedScene::pack()           シーンファイル保存
                              │                               │
                              ▼                               ▼
                    POST_SAVE通知                 保存後処理
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| node.h | `scene/main/node.h` | ソース | 通知定数定義 |
| node.cpp | `scene/main/node.cpp` | ソース | Node基底クラス実装 |
| editor_node.cpp | `editor/editor_node.cpp` | ソース | エディタノード・保存処理 |
| skeleton_3d.cpp | `scene/3d/skeleton_3d.cpp` | ソース | Skeleton3D実装（savingフラグ） |
| control.cpp | `scene/gui/control.cpp` | ソース | Control実装 |
| skeleton_2d.cpp | `scene/2d/skeleton_2d.cpp` | ソース | Skeleton2D実装 |
| spring_bone_simulator_3d.cpp | `scene/3d/spring_bone_simulator_3d.cpp` | ソース | SpringBoneSimulator実装 |
