Files
aibis-dream/Assets/Scripts/SaveSystem
..

SaveSystem 存档快照层

当前代码包含 P1 快照层、P2 槽位/落盘基础版、P3 可存点判定基础版、P4 读档编排基础版。设计文档见 Docs/存档系统设计方案.md§4.1 / D6 描述读档编排模型)。

职责边界

模块 位置 做什么
Yarn 变量 Game Loop/YarnVariableStorage.cs 运行时 $ / $global_ 读写
本目录 SaveSystem/ 快照结构、捕获/还原、槽位落盘、流程编排
深度维修(旧) DeepRepairDataRegistry.cs FixSystem 等 IDataP5 前临时保留
槽位 / 截图 SlotManager / SlotThumbnailCapture P2 基础版:slot_0 自动档、slot_1..8 手动档、sidecar、缩略图、原子写

不要再往 YarnVariableStorage 上堆存档逻辑。

对外入口

// 自动存档(节点进入事件触发)
yield return SaveRestoreOrchestrator.AutoSaveRoutine(nodeName);

// 手动存档:复制最近自动档
SaveRestoreOrchestrator.CreateManualSlot(slotIndex);

// 槽位读档
yield return SaveRestoreOrchestrator.RestoreFromSlot(slotIndex);

// 文件读档(旧档 / 调试)
yield return SaveRestoreOrchestrator.RestoreFromFile(path);

// 仅内存快照
var snap = SnapshotService.Capture();
yield return SnapshotService.Restore(snap);

// Yarn 变量(与存档无关)
YarnVariableStorage.Instance.SetValue("$foo", 1f);

正式槽位路径:persistentDataPath/AllOurBrokenParts/saves/slot_x/{snapshot.json, meta.json, thumbnail.png}

P1 测试落盘路径:persistentDataPath/AllOurBrokenParts/snapshot_test/latest_snapshot.json(首次访问槽位系统时会尝试迁移到 slot_0

目录结构

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 统一返回 IEnumeratorSnapshotRestore 对每个 Provider 做 yield return——这是受旧 IData.Load() 影响的临时 scaffolding。终态见设计文档 §4.1 / D6P4 实施前应 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:淡入淡出等演出时序
  • 核心层sceneanchoryarnVariables)由 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 RestoreSnapshotyield 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.csSnapshotProviderIds 区域增加稳定 id,并加入 RequiredForCapture(若 P1 必须存)。
  3. 在对应 Manager 实现 CaptureSnapshot / RestoreSnapshot默认 sync void;仅确有 async 加载/状态切换时用 IEnumerator 并向编排层上报)。
  4. 新建 Providers/XxxSnapshotProvider.cs 实现 ISnapshotProviderP4 后:sync 默认 + 显式 async 接口)。
  5. SnapshotRegistry.EnsureInitializedRegister
  6. SnapshotRestore.CoerceSectionDto 增加 JObject 转换分支。

禁止在 Provider 内私自 StartCoroutine 而不纳入编排 Barrier。

例外scene(场景加载)与 anchor(章节+节点重进)属于核心叙事坐标,由 SnapshotCapture / SnapshotRestore 框架直管,不走 Provider 注册。新增「核心层」字段需修改 SaveSnapshot 根对象与对应编排方法。

Fix 场景快照(P1 接口层完成)

Fix 场景(维修场景)的快照存储在 sections["fix"],由 FixSnapshotProviderorder 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 已落地的接口

  • FixSnapshotDtostate、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 基础版已落地:槽位、原子写、meta sidecar、缩略图、latest_slot、P1 测试档迁移;正式 UI 接入仍属 P6
P3 基础版已落地:onNodeStart 判定、tag 白/黑名单、no_save、读档/暂停门控;瞬跳 content 排查与无活跃 Yarn 边界仍待补
P4 基础版已落地:Provider sync/async 契约、Phase + Barrier 编排、读档自动存档抑制、Timeline Addressable 可等待恢复、验证窗口读档入口
P5 deepRepair 段、维修模块清单;还原模型单独定案,不默认套用 P1 Provider 协程链
P6 正式存 / 读档 UI、继续游戏、新游戏覆盖自动档、游戏中读档确认等玩家流程