# Godot文件存储 **本文引用的文件** - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs) - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs) - [IFileStorage.cs](file://GFramework.Game.Abstractions/storage/IFileStorage.cs) - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs) - [README.md(Godot存储模块)](file://GFramework.Godot/storage/README.md) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本指南围绕Godot引擎的文件存储系统,聚焦GodotFileStorage类的设计与实现,系统阐述其对res://、user://虚拟路径与普通文件系统路径的支持机制;详解基于key的细粒度锁与ConcurrentDictionary的线程安全策略;深入解析Delete、Exists、Read、Write等核心方法的实现逻辑及异步版本的适用场景;说明路径处理的安全机制(路径验证、非法字符清理、相对路径限制);并提供完整的API使用示例、错误处理、性能优化建议与最佳实践,以及与ISerializer接口的集成方式与序列化策略。 ## 项目结构 Godot文件存储位于GFramework.Godot模块中,核心实现为GodotFileStorage,配合路径扩展工具与序列化器接口完成跨平台与Godot生态的适配。 ```mermaid graph TB subgraph "Godot模块" GFS["GodotFileStorage.cs"] GPE["GodotPathExtensions.cs"] end subgraph "抽象层" IS["IStorage.cs"] IF["IFileStorage.cs"] SER["ISerializer.cs"] end subgraph "具体实现" JS["JsonSerializer.cs"] end subgraph "文档" DOC["README.mdGodot存储模块"] end GFS --> IS GFS --> SER GFS --> GPE JS --> SER DOC --> GFS DOC --> GPE ``` 图表来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L1-L31) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs#L1-L22) - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs#L1-L72) - [IFileStorage.cs](file://GFramework.Game.Abstractions/storage/IFileStorage.cs#L1-L9) - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs#L1-L25) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs#L1-L29) - [README.md(Godot存储模块)](file://GFramework.Godot/storage/README.md#L1-L281) 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L1-L31) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs#L1-L22) - [README.md(Godot存储模块)](file://GFramework.Godot/storage/README.md#L1-L281) ## 核心组件 - GodotFileStorage:实现IStorage接口,提供线程安全的文件读写、存在性检查与删除能力,支持res://、user://与普通文件系统路径。 - GodotPathExtensions:提供IsGodotPath、IsUserPath、IsResPath等扩展方法,用于识别Godot虚拟路径。 - IStorage/ISerializer:定义统一的存储接口与序列化契约,便于替换不同序列化器(如JSON)。 - JsonSerializer:具体序列化实现,负责对象与字符串之间的双向转换。 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L15-L31) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs#L3-L22) - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs#L9-L72) - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs#L8-L25) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs#L9-L29) ## 架构总览 GodotFileStorage通过以下层次协作: - 接口层:IStorage定义统一API;IFileStorage继承IStorage,形成Godot专用的文件存储接口。 - 实现层:GodotFileStorage实现IStorage,内部组合ISerializer进行序列化;使用GodotPathExtensions判断路径类型。 - 平台适配:Godot虚拟路径使用FileAccess/DirAccess;普通路径使用标准.NET文件API。 - 线程安全:以路径为key的细粒度锁,结合ConcurrentDictionary管理锁对象,避免全局锁竞争。 ```mermaid classDiagram class IStorage { +Exists(key) bool +ExistsAsync(key) Task~bool~ +Read~T~(key) T +Read~T~(key, defaultValue) T +ReadAsync~T~(key) Task~T~ +Write~T~(key, value) void +WriteAsync~T~(key, value) Task +Delete(key) void } class IFileStorage { } class GodotFileStorage { -_keyLocks : ConcurrentDictionary~string, object~ -_serializer : ISerializer +Exists(key) bool +ExistsAsync(key) Task~bool~ +Read~T~(key) T +Read~T~(key, defaultValue) T +ReadAsync~T~(key) Task~T~ +Write~T~(key, value) void +WriteAsync~T~(key, value) Task +Delete(key) void -ToAbsolutePath(key) string -GetLock(path) object } class GodotPathExtensions { +IsUserPath(path) bool +IsResPath(path) bool +IsGodotPath(path) bool } class ISerializer { +Serialize~T~(value) string +Deserialize~T~(data) T } class JsonSerializer { +Serialize~T~(value) string +Deserialize~T~(data) T } IFileStorage --|> IStorage GodotFileStorage ..|> IStorage GodotFileStorage --> ISerializer GodotFileStorage --> GodotPathExtensions JsonSerializer ..|> ISerializer ``` 图表来源 - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs#L9-L72) - [IFileStorage.cs](file://GFramework.Game.Abstractions/storage/IFileStorage.cs#L1-L9) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L15-L31) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs#L3-L22) - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs#L8-L25) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs#L9-L29) ## 详细组件分析 ### GodotFileStorage类设计与实现 - 设计理念 - 针对Godot生态定制:原生支持res://(只读资源)、user://(可读写用户数据)与普通文件系统路径。 - 线程安全:以“路径”为key的细粒度锁,避免全局锁竞争,提升并发吞吐。 - 序列化解耦:通过ISerializer抽象,允许切换JSON、二进制等序列化策略。 - 关键字段与构造 - _keyLocks:ConcurrentDictionary,按路径维护独立锁对象。 - _serializer:ISerializer实例,负责对象与字符串的序列化/反序列化。 - 构造函数:注入ISerializer,若为空则抛出异常。 - 路径处理与安全 - ToAbsolutePath:规范化斜杠、禁止“..”、区分Godot路径与普通路径;对普通路径进行段级非法字符清理与目录自动创建。 - SanitizeSegment:将无效文件名字符替换为下划线,降低跨平台兼容性问题。 - GetLock:按路径获取或新增锁对象,避免锁字典无限增长后及时清理。 - 核心方法实现 - Exists/ExistsAsync:根据路径类型选择FileAccess.FileExists或File.Exists;异步版本采用Task.FromResult包装。 - Read/Read/ReadAsync:先打开文件,再调用_serializer.Deserialize;普通路径使用UTF-8编码读取文本。 - Write/WriteAsync:先序列化,再写入;Godot路径使用FileAccess,普通路径使用File.WriteAllText。 - Delete:支持Godot路径(DirAccess.RemoveAbsolute)与普通路径(File.Delete),完成后尝试移除锁。 - 异步版本使用场景 - ReadAsync:适合长时间IO或需要避免阻塞UI线程的场景;内部仍使用lock保护,避免竞态。 - WriteAsync:适合批量写入或后台持久化;内部仍使用lock,保证原子性。 - 错误处理 - ArgumentException:空路径、包含“..”、无效键。 - FileNotFoundException:读取不存在文件时抛出。 - IOException:Godot路径删除失败时抛出。 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L15-L31) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L39-L66) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L132-L143) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L164-L188) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L217-L244) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L256-L276) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L85-L114) ### 路径处理与安全机制 - 路径类型 - res://:只读资源路径,随游戏打包;使用FileAccess.Open/Exists。 - user://:可读写用户数据路径,Godot用户目录;使用FileAccess.Open/Exists/RemoveAbsolute。 - 普通路径:完整文件系统访问;使用File.Exists/ReadAllText/WriteAllText/Delete。 - 安全控制 - 非法字符清理:SanitizeSegment将无效文件名字符替换为下划线。 - 相对路径限制:禁止“..”,防止路径逃逸。 - 目录自动创建:普通路径写入前确保父目录存在。 - 路径验证流程图 ```mermaid flowchart TD Start(["输入存储键"]) --> Normalize["规范化斜杠 '/'"] Normalize --> CheckDotDot{"包含 '..' ?"} CheckDotDot --> |是| ThrowArg["抛出ArgumentException"] CheckDotDot --> |否| IsGodot{"是否Godot路径(res:// 或 user://)?"} IsGodot --> |是| ReturnGodot["直接返回Godot路径"] IsGodot --> |否| Split["按'/'分割并去空"] Split --> ValidateSegs{"段数 > 0 ?"} ValidateSegs --> |否| ThrowArg2["抛出ArgumentException"] ValidateSegs --> |是| BuildDir["组合目录段"] BuildDir --> CreateDir["确保目录存在"] CreateDir --> Combine["拼接文件名"] Combine --> ReturnAbs["返回绝对路径"] ThrowArg --> End(["结束"]) ThrowArg2 --> End ReturnGodot --> End ReturnAbs --> End ``` 图表来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L85-L114) 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L77-L114) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs#L3-L22) ### 线程安全与并发模型 - 细粒度锁 - _keyLocks:以“绝对路径”为key的锁字典,每个文件路径拥有独立锁对象。 - GetLock:按路径获取或新增锁对象;Delete完成后TryRemove移除无用锁,避免内存膨胀。 - 并发策略 - 读写操作均在对应路径锁内执行,避免同一文件的并发冲突。 - 不同文件路径可并发访问,显著降低锁竞争。 - 锁生命周期 - 仅在必要时持有锁,尽量缩短临界区;读取阶段可共享读取,写入阶段独占。 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L20-L31) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L121-L122) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L39-L66) ### 与ISerializer接口的集成 - ISerializer契约 - Serialize:将对象序列化为字符串。 - Deserialize:将字符串反序列化为对象。 - JsonSerializer实现 - 使用Newtonsoft.Json进行序列化/反序列化,默认UTF-8编码。 - 反序列化失败时抛出ArgumentException。 - 集成方式 - GodotFileStorage在Read/Write中调用ISerializer,实现与具体序列化策略解耦。 - 可替换为二进制序列化器以获得更高性能与更小体积。 章节来源 - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs#L8-L25) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs#L9-L29) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L22-L31) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L186-L187) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L263-L275) ### API使用示例与最佳实践 - 基本使用 - 写入用户数据:Write("user://player.dat", userData) - 读取用户数据:Read("user://player.dat") - 异步操作 - 异步写入配置:WriteAsync("user://config.json", config) - 异步读取配置:ReadAsync("user://config.json") - 不同路径类型 - 读取资源:Read("res://levels/level_001.json") - 存储用户存档:Write("user://saves/slot_001.dat", saveData) - 存储调试信息:Write("logs/debug_...json", debugLog) - 存在性检查与默认值 - Exists("user://settings.json") + Read(key, default) - 错误处理 - 捕获FileNotFoundException:文件不存在时回退默认值或初始化新数据。 - 捕获ArgumentException:路径不合法(空、包含“..”、无效键)。 - 捕获IOException:Godot路径删除失败或写入权限不足。 - 性能优化建议 - 批量读写时优先使用异步方法,避免阻塞主线程。 - 避免频繁的小文件操作,合并写入或使用缓存。 - 选择合适的序列化器:JSON便于调试,二进制更高效。 - 最佳实践 - 资源文件使用res://,用户数据使用user://,临时调试使用普通路径。 - 总是处理FileNotFoundException,使用带默认值的Read重载。 - 在高并发场景下,尽量减少锁持有时间,避免长耗时操作在锁内执行。 章节来源 - [README.md(Godot存储模块)](file://GFramework.Godot/storage/README.md#L96-L177) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L132-L143) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L164-L188) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L217-L244) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L256-L276) ## 依赖关系分析 - 组件耦合 - GodotFileStorage依赖IStorage接口与ISerializer接口,耦合度低,便于替换实现。 - 通过GodotPathExtensions识别Godot路径,保持路径处理逻辑与业务解耦。 - 外部依赖 - Godot.FileAccess/DirAccess:用于Godot虚拟路径的文件操作。 - System.IO:用于普通文件系统路径的文件操作。 - Newtonsoft.Json:JsonSerializer的具体实现依赖。 - 循环依赖 - 未发现循环依赖;接口与实现分离清晰。 ```mermaid graph LR GFS["GodotFileStorage"] --> IS["IStorage"] GFS --> SER["ISerializer"] GFS --> GPE["GodotPathExtensions"] JS["JsonSerializer"] --> SER GFS --> GD["Godot.FileAccess/DirAccess"] GFS --> IO["System.IO"] ``` 图表来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L1-L7) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L15-L31) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs#L1-L2) - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs#L1-L2) - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs#L1-L2) 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L1-L7) - [JsonSerializer.cs](file://GFramework.Game/serializer/JsonSerializer.cs#L1-L2) - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs#L1-L2) - [ISerializer.cs](file://GFramework.Game.Abstractions/serializer/ISerializer.cs#L1-L2) ## 性能考量 - 锁机制 - 每个文件路径独立锁,减少锁竞争,提高并发性能。 - 读写操作串行化,避免数据损坏。 - 文件访问 - Godot虚拟路径使用FileAccess/DirAccess,适合资源与用户数据场景。 - 普通路径使用标准.NET文件API,支持完整文件系统访问。 - 内存使用 - 锁对象使用ConcurrentDictionary管理,按需创建,避免内存泄漏。 - Delete完成后TryRemove移除锁,防止字典无限增长。 - 异步策略 - ReadAsync/WriteAsync在锁范围内执行,避免竞态;异步主要用于避免阻塞UI线程。 - 序列化开销 - JSON可读性好,调试友好;二进制序列化器可显著降低体积与提升性能。 章节来源 - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L20-L31) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L44-L66) - [README.md(Godot存储模块)](file://GFramework.Godot/storage/README.md#L205-L222) ## 故障排查指南 - 常见异常 - ArgumentException:路径为空、包含“..”、无效键。 - FileNotFoundException:读取不存在的文件。 - IOException:Godot路径删除失败或写入权限不足。 - 排查步骤 - 路径合法性:确认键不包含“..”,且符合目标路径类型(res://、user://或普通路径)。 - 权限问题:user://路径需确保Godot用户目录可写;普通路径需检查磁盘权限。 - 文件状态:使用Exists检查文件是否存在;不存在时使用默认值或初始化数据。 - 锁竞争:避免在锁内执行长耗时操作;必要时拆分逻辑。 - 示例场景 - 读取存档失败:捕获FileNotFoundException,创建默认存档并写入。 - 删除失败:捕获IOException,检查Godot错误码并记录日志。 章节来源 - [README.md(Godot存储模块)](file://GFramework.Godot/storage/README.md#L223-L256) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L87-L94) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L175-L177) - [GodotFileStorage.cs](file://GFramework.Godot/storage/GodotFileStorage.cs#L267-L268) ## 结论 GodotFileStorage通过明确的路径类型识别、严格的路径安全控制、细粒度的锁机制与ISerializer解耦,为Godot应用提供了可靠、高性能的文件存储方案。其对res://、user://与普通路径的统一支持,使开发者能够在不同场景下灵活选择存储位置;异步API与序列化策略的可替换性进一步提升了系统的可维护性与性能潜力。遵循本文的最佳实践与故障排查建议,可在复杂多线程环境下稳定地使用该存储系统。 ## 附录 - API参考(来自接口定义) - Exists/ExistsAsync:检查键是否存在。 - Read/Read/ReadAsync:读取并反序列化对象。 - Write/WriteAsync:序列化并写入对象。 - Delete:删除文件(GodotFileStorage已实现)。 - 路径扩展 - IsGodotPath/IsUserPath/IsResPath:辅助判断路径类型。 章节来源 - [IStorage.cs](file://GFramework.Core.Abstractions/storage/IStorage.cs#L9-L72) - [GodotPathExtensions.cs](file://GFramework.Godot/extensions/GodotPathExtensions.cs#L3-L22)