Files
aibis-dream/Assets/Scripts/SaveSystem/README.md
T
dingyuntian afd062bda2 docs(save): 更新 SaveSystem README
同步 README 目录结构:
- 反映已合并的 P1 文件(SaveSnapshotSummary、SnapshotBootstrap 等)
- 补充 P2 新增文件(SlotTypes、SlotFileSystem、SlotManager、CloudSave)
- 更新 Provider 注册与 section 反序列化指引
2026-06-12 17:41:23 +08:00

191 lines
9.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SaveSystem 存档快照层
> P1 实现:纯数据快照 + 逐项还原。设计文档见 [`Docs/存档系统设计方案.md`](../../../Docs/存档系统设计方案.md)**§4.1 / D6** 描述读档编排终态)。
## 职责边界
| 模块 | 位置 | 做什么 |
| --- | --- | --- |
| Yarn 变量 | `Game Loop/YarnVariableStorage.cs` | 运行时 `$` / `$global_` 读写 |
| **本目录** | `SaveSystem/` | 快照结构、捕获/还原、落盘、流程编排 |
| 深度维修(旧) | `DeepRepairDataRegistry.cs` | FixSystem 等 IDataP5 前临时保留 |
| 槽位 / 截图 | P2 待做 | 接管 `SnapshotPersistence` 与目录布局 |
**不要**再往 `YarnVariableStorage` 上堆存档逻辑。
## 对外入口
```csharp
// 存盘(<<auto_save>>、调试)
SaveRestoreOrchestrator.SaveToTestPath();
// 读盘
yield return SaveRestoreOrchestrator.RestoreFromFile(path);
// 仅内存快照(P2 手动档固化等)
var snap = SnapshotService.Capture();
yield return SnapshotService.Restore(snap);
// Yarn 变量(与存档无关)
YarnVariableStorage.Instance.SetValue("$foo", 1f);
```
P1 测试落盘路径:`persistentDataPath/AllOurBrokenParts/snapshot_test/latest_snapshot.json`
## 目录结构
```
SaveSystem/
├── README.md ← 本文件
├── ISnapshotProvider.cs 契约:Capture / RestoreP1 临时形态,见下)
├── SaveSnapshot.cs 快照根对象 + 各 section DTO + SaveSnapshotSummary
├── SnapshotRegistry.cs provider 稳定 id、注册表、启动注册
├── SnapshotCapture.cs 组装 SaveSnapshotscene / anchor 由框架直管)
├── SnapshotRestore.cs 写回运行时;含 section JObject → 强类型转换
├── SnapshotService.cs Capture / Restore 对外 API
├── SnapshotPersistence.cs 文件读写、JSON 序列化、旧档检测
├── SaveRestoreOrchestrator.cs UI + 落盘 + 读盘编排
├── DeepRepairSnapshot.cs 深度维修占位(P5)
├── DeepRepairDataRegistry.cs 旧 IData 容器(P5 前)
├── ScreenSnapshotHelper.cs screen section 实现细节
├── SlotTypes.cs P2:槽位常量、sidecar、UI 视图模型
├── SlotFileSystem.cs P2:槽位路径 + 原子写
├── SlotManager.cs P2:槽位业务核心
├── SlotThumbnailCapture.cs P2:截图 helper
├── CloudSave/ P2:云存档适配器接口与协调器
│ ├── ICloudSaveBackend.cs
│ └── CloudSaveManager.cs
└── Providers/ 表现层子系统薄适配层
├── EnvironmentSnapshotProvider.cs order 20
├── ActorSnapshotProvider.cs order 40
├── AudioSnapshotProvider.cs order 50
├── TimelineSnapshotProvider.cs order 60
├── FixSnapshotProvider.cs order 65
└── ScreenSnapshotProvider.cs order 70
```
## 存盘 / 读盘流程
```
存盘:
SaveRestoreOrchestrator
→ SnapshotService.Capture()
→ SnapshotCapture(场景 + 锚点 + Yarn 变量 + 各 Provider
→ SnapshotPersistence.Save()
读盘:
SaveRestoreOrchestrator
→ SnapshotPersistence.Load() (或 legacy 分支)
→ SnapshotService.Restore()
→ SnapshotRestore(见「读档还原编排」)
```
## 读档还原编排(终态 vs P1 代码)
> **勿将当前代码当作终态。** P1 中 `ISnapshotProvider.Restore` 统一返回 `IEnumerator``SnapshotRestore` 对每个 Provider 做 `yield return`——这是受旧 `IData.Load()` 影响的**临时 scaffolding**。终态见设计文档 **§4.1 / D6**P4 实施前应 refactor。
### 终态:Phase + Barrier
读档分阶段推进,仅在**必须等待**的边界上 `yield`
```
Phase 0 Yarn 变量(sync
Phase 1 场景加载(BarrierLoadSceneAsync)← 框架直管,不走 Provider
Phase 1.5 设置章节 SOsync)← 框架直管,从 anchor.sceneSoName 读取
Phase 2 env / actor / audio / timeline / fix / screenProvider 按 RestoreOrder
Phase 2 可选 Barrier(如 Timeline Addressable 须显式等待)
Phase 3 加载对话工程 + RestoreAnchorBarrierStartDialogue)← 框架直管
Phase 4 P4:淡入淡出等演出时序
```
- **核心层**`scene``anchor``yarnVariables`)由 `SnapshotRestore` 框架直管,不通过 Provider 注册。
- **表现层**`sections` 内各子系统)通过 `ISnapshotProvider` 扩展;`RestoreOrder` 表示同 Phase 内的建议顺序或软依赖。
- **逐项还原(D1)**指各子系统各自写回状态,**不是** Provider 之间逐步 `yield return`
### P1 临时形态 vs P4 目标
| | P1(当前) | P4 目标 |
| --- | --- | --- |
| 核心层还原 | 硬编码在 `SnapshotRestore` | 保持框架直管 |
| Provider 还原 | 全部 `IEnumerator Restore` | 默认 `void Restore`;仅需 async 加载的实现显式协程 |
| 编排 | `foreach` 逐步 `yield return` | Phase 编排,仅 Barrier 步骤 `yield` |
| Manager | 部分 `IEnumerator RestoreSnapshot``yield break` | 默认 `void`;真有 async 才保留协程 |
### 已知偏离(tech debt
- `DirectorHandler` 在 Addressable Timeline 路径下内部 `StartCoroutine`,Provider 已返回,编排层无法感知——P4 应改为可等待路径。
- P2/P3 新增代码**不要**再复制「全 Provider 协程链」模式。
## 快照 JSON 结构(schemaVersion = 1
| 字段 | 含义 |
| --- | --- |
| `scene` | 当前场景名(Addressable key),读档时最先加载 |
| `anchor` | 恢复锚点:`sceneSoName` + `yarnProjectId` + `nodeName`;最后阶段加载对话并重进节点 |
| `yarnVariables` | floats / strings / bools |
| `sections` | key 为 `SnapshotProviderIds`,值为各 DTO**表现层状态(env / actor / audio / timeline / fix / screen** |
| `deepRepair` | **P5 深度维修**BlockPuzzle / Cutting / AnalysisMode 等)。P1 阶段已预留 `DeepRepairSnapshotDto`,但详细子系统状态暂不捕获;待存档系统主干稳定后再补充并测试 |
> 宏观信息(场景、章节、节点)直接位于 `SaveSnapshot` 根对象,不再冗余到 `sections`;槽位 / 「继续游戏」UI 需要展示时,从 `scene` / `anchor` 直接读取。
## 新增一个可存子系统(表现层)
表现层子系统通过 Provider 注册到 `sections`
1.`SaveSnapshot.cs` 增加 DTO 类。
2.`SnapshotRegistry.cs``SnapshotProviderIds` 区域增加稳定 id,并加入 `RequiredForCapture`(若 P1 必须存)。
3. 在对应 Manager 实现 `CaptureSnapshot` / `RestoreSnapshot`**默认 sync `void`**;仅确有 async 加载/状态切换时用 `IEnumerator` 并向编排层上报)。
4. 新建 `Providers/XxxSnapshotProvider.cs` 实现 `ISnapshotProvider`P4 后:sync 默认 + 显式 async 接口)。
5.`SnapshotRegistry.EnsureInitialized``Register`
6.`SnapshotRestore.CoerceSectionDto` 增加 JObject 转换分支。
**禁止**在 Provider 内私自 `StartCoroutine` 而不纳入编排 Barrier。
> **例外**`scene`(场景加载)与 `anchor`(章节+节点重进)属于**核心叙事坐标**,由 `SnapshotCapture` / `SnapshotRestore` 框架直管,不走 Provider 注册。新增「核心层」字段需修改 `SaveSnapshot` 根对象与对应编排方法。
## Fix 场景快照(P1 接口层完成)
Fix 场景(维修场景)的快照存储在 `sections["fix"]`,由 `FixSnapshotProvider`order 65)管理。
### 为什么 FixStateMachine 放在 sections 而不是 deepRepair
FixStateMachine 不仅管理深度维修(Eye、Memory、EmoWave 等),还管理 **Clinic(诊所)****BodyModule(插线维修)** 等非深度维修状态。把它放在 `sections` 中作为表现层子系统之一,可以避免概念错位。
### 双入口设计:Enter vs EnterImmediate
Fix 场景各 State 的 `Enter()` 通常包含相机过渡、Timeline 播放、Fade 等动画。读档时需要跳过这些动画直接到达终态,因此为 `IFixState` 增加了 `EnterImmediate()` 入口:
- 正常游戏流程 → `Enter()`(完整动画)
- 读档恢复 → `SwitchStateImmediate()``EnterImmediate()`(跳过动画)
读档时 `SwitchStateImmediate` **不调用前一个状态的 Exit()**,因为读档本质是覆盖当前状态,不需要清理。
### P1 已落地的接口
- `FixSnapshotDto`state、args、moduleState、eyeColorState、isSystemOn、currentRepairSystemType
- `IFixState` 接口 + `EnterImmediate()` 默认实现
- `FixStateMachine.SwitchStateImmediate()`
- `FixSystemCenter.CaptureSnapshot()` / `RestoreSnapshot()`
- `FixSnapshotProvider` 注册到 `SnapshotRegistry.EnsureInitialized`
### 尚未实现(P4/P5
- 各 State 的 `EnterImmediate()` 具体逻辑(当前默认 `yield break`
- 深度维修子系统详细状态(BlockPuzzle grid、Cutting 进度等)→ P5 `deepRepair`
- Fix 场景读档的 Fade 时序 → P4 编排层
## 相关阶段(未在本目录完整实现)
| 阶段 | 内容 |
| --- | --- |
| P2 | 槽位、原子写、截图 sidecar → 扩展 `SnapshotPersistence` |
| P3 | 可存点判定、写盘门控 |
| P4 | 按 §4.1 重写 `SnapshotRestore`Phase + Barrier);refactor Provider 契约;`SaveRestoreOrchestrator` 接入淡入淡出 |
| P5 | `deepRepair` 段、维修模块清单;还原模型单独定案,不默认套用 P1 Provider 协程链 |