GFramework/docs/zh-CN/godot/storage.md
gewuyou 85f7c1707e docs(game): 同步场景与宿主入口配置边界
- 补充 Scene 与 UI 入口对配置系统正式边界页的指引

- 明确 oneOf、anyOf 与非 false additionalProperties 不属于默认采用路径

- 更新 Godot storage 入口对 VS Code 工具辅助层与 raw YAML 回退路径的说明
2026-04-30 13:25:29 +08:00

5.8 KiB
Raw Blame History

title, description
title description
Godot 存储系统 以当前 GFramework.Godot 源码与 CoreGrid 接线为准,说明 GodotFileStorage 的职责、路径边界和最小接入方式。

Godot 存储系统

GFramework.Godot 在存储这一层提供的核心入口只有 GodotFileStorage

它实现 GFramework.Game 侧统一的 IStorage 契约,负责把序列化后的读写、目录列举和路径处理接到 Godot 的 res://user:// 和普通文件系统路径上而不是另外提供一套独立的“Godot 专属存档框架”。

当前公开入口

GodotFileStorage

GodotFileStorage 的当前职责比较集中:

  • 对外暴露 IStorage 约定的 ReadWriteExistsDelete、目录列举与目录创建能力
  • 识别并保留 Godot 虚拟路径:res://user://
  • 对普通文件系统路径做段级清理,并拒绝包含 .. 的非法 key
  • 使用 IAsyncKeyLockManager 对“绝对路径 / Godot 路径”做按 key 细粒度串行化

构造函数默认会在未注入锁管理器时创建内部 AsyncKeyLockManager。这意味着:

  • 同一个 GodotFileStorage 实例内,不同文件可以并发访问
  • 同一个目标路径的读写 / 删除会被串行化
  • 锁作用域只限当前进程内的当前实例,不是跨进程文件锁

路径语义

res://

res:// 更适合作为只读资源或配置源目录。

当前实现不会阻止你把它传给 ReadAsyncExistsAsync 之类的方法,但在导出后的 Godot 项目里,res:// 通常不应被当作用户可写存储根目录。存档、设置和运行时缓存应优先落到 user://

user://

user:// 是当前推荐的可写路径:

  • 用户设置
  • 存档
  • 运行时缓存
  • 导出后仍需要读写的 JSON / YAML / 二进制数据

如果调用 ListDirectoriesAsync()ListFilesAsync() 时传入空字符串,当前实现会默认从 user:// 根开始列举。

普通文件系统路径

当 key 不是 Godot 路径时,GodotFileStorage 会:

  1. \ 统一成 /
  2. 拒绝包含 .. 的 key
  3. 按路径段清理非法文件名字符
  4. 在写入或建目录前自动补父目录

这条路径更适合测试、桌面工具链或显式指定外部目录的宿主环境,不建议在项目业务层自己重新拼装一套路径清理逻辑。

最小接入路径

当前更常见的接法,是先注册同一个序列化器和存储实例,再让设置仓库、存档仓库等上层组件复用它:

using GFramework.Core.Abstractions.Serializer;
using GFramework.Core.Abstractions.Storage;
using GFramework.Game.Abstractions.Data;
using GFramework.Game.Storage;
using GFramework.Godot.Storage;
using Godot;

var jsonSerializer = new JsonSerializer();
architecture.RegisterUtility<ISerializer>(jsonSerializer);

var storage = new GodotFileStorage(jsonSerializer);
architecture.RegisterUtility<IStorage>(storage);

architecture.RegisterUtility(new UnifiedSettingsDataRepository(
    storage,
    jsonSerializer,
    new DataRepositoryOptions
    {
        BasePath = ProjectSettings.GetSetting("application/config/save/setting_path").AsString(),
        AutoBackup = true
    }));

architecture.RegisterUtility<ISaveRepository<GameSaveData>>(new SaveRepository<GameSaveData>(
    storage,
    new SaveConfiguration
    {
        SaveRoot = ProjectSettings.GetSetting("application/config/save/save_path").AsString(),
        SaveSlotPrefix = ProjectSettings.GetSetting("application/config/save/save_slot_prefix").AsString(),
        SaveFileName = ProjectSettings.GetSetting("application/config/save/save_file_name").AsString()
    }));

这里的分工是:

  • GodotFileStorage 负责底层 key -> 文件读写
  • UnifiedSettingsDataRepository 负责设置节聚合与持久化
  • SaveRepository<TSaveData> 负责存档结构和保存槽位语义

不要把 GodotFileStorage 本身写成“设置系统”或“存档系统”的 owner。

什么时候应该改看别的入口

配置 YAML / schema 文本加载

如果你的目标是读取 res:// 下的 YAML 配置,并在导出态同步到运行时缓存,可继续阅读 Game 配置系统GodotYamlConfigLoader 的接法。

这类场景的重点不是通用键值存储,而是:

  • res://user:// 缓存切换
  • 生成器表元数据
  • 热重载可用性边界

如果这里涉及 schema 采用边界,也应以 Game 配置系统 为正式说明页,而不是把 GodotFileStorage 所在的宿主接线页理解成配置边界定义。默认采用路径之外的典型场景包括 oneOf / anyOf、 非 falseadditionalProperties,以及其他更复杂的 schema shape。VS Code 工具只是辅助编辑与预览层; 遇到这些情况时,应直接回到 raw YAML 和 schema 本体设计处理。

通用存储契约

宿主无关的 IStorageScopedStorageFileStorage 和统一数据仓库语义,可继续阅读 Game 存储系统

本页只补 Godot 宿主差异,不重复维护一份跨宿主 API 手册。

当前边界

  • 同步 Read / Write / Delete / Exists 只是对异步方法的阻塞包装;在带同步上下文的宿主里,优先使用异步 API
  • GodotFileStorage 不负责文件扩展名约定、作用域前缀或保存槽位策略,这些属于上层 repository / scoped storage
  • 路径安全只覆盖当前 key 的格式校验与路径段清理,不代替业务层的目录规划
  • 当前实现支持目录列举与目录创建,但没有额外的“监视目录变化”或“自动迁移目录结构”能力

继续阅读

  1. Godot 运行时集成
  2. Game 存储系统
  3. Game 配置系统
  4. Godot 集成教程