218 lines
13 KiB
Markdown
218 lines
13 KiB
Markdown
# 本地存储管理
|
||
|
||
<cite>
|
||
**本文档引用文件**
|
||
- [config.json](file://assets/resources/config.json)
|
||
- [Oops.ts](file://extensions/oops-plugin-framework/assets/core/Oops.ts)
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts)
|
||
- [StorageManager.ts](file://extensions/oops-plugin-framework/assets/core/common/storage/StorageManager.ts)
|
||
- [Initialize.ts](file://assets/script/game/initialize/Initialize.ts)
|
||
- [SingletonModuleComp.ts](file://assets/script/game/common/SingletonModuleComp.ts)
|
||
- [WxCloudApi.ts](file://assets/script/game/wx_clound_client_api/WxCloudApi.ts)
|
||
- [Main.ts](file://assets/script/Main.ts)
|
||
</cite>
|
||
|
||
## 目录
|
||
1. [简介](#简介)
|
||
2. [项目结构](#项目结构)
|
||
3. [核心配置项解析](#核心配置项解析)
|
||
4. [本地数据加密机制](#本地数据加密机制)
|
||
5. [配置加载与初始化流程](#配置加载与初始化流程)
|
||
6. [本地缓存与云端数据策略](#本地缓存与云端数据策略)
|
||
7. [性能与网络配置](#性能与网络配置)
|
||
8. [故障处理机制](#故障处理机制)
|
||
9. [结论](#结论)
|
||
|
||
## 简介
|
||
本文档深入解析基于 Cocos 引擎的 `heros` 项目中的本地存储机制。重点围绕 `config.json` 文件中的核心配置项,包括 `version`、`package`、`localDataKey`、`localDataIv`、`httpServer`、`httpTimeout` 和 `frameRate` 的作用、加载时机及使用场景。文档详细阐述了基于 `crypto-es` 库的本地数据加密存储方案的设计思路,以及如何通过 `localDataKey` 和 `localDataIv` 实现数据保护。同时,分析了本地缓存与云端数据的优先级策略,以及在离线模式下的容错处理机制。
|
||
|
||
**Section sources**
|
||
- [config.json](file://assets/resources/config.json)
|
||
|
||
## 项目结构
|
||
项目采用模块化设计,主要结构如下:
|
||
- `assets/resources/config.json`:核心配置文件,包含游戏版本、包名、加密密钥、网络地址等全局配置。
|
||
- `assets/script/`:存放所有 TypeScript 脚本,其中 `game/initialize/` 是游戏初始化逻辑的核心。
|
||
- `extensions/oops-plugin-framework/`:项目所依赖的 `Oops` 框架,提供了本地存储、网络、ECS 等核心功能的封装。
|
||
- `assets/resources/language/json/`:多语言资源文件。
|
||
- `build-templates/wechatgame/cloud_functions/`:微信云函数相关配置。
|
||
|
||
**Section sources**
|
||
- [config.json](file://assets/resources/config.json)
|
||
- [Main.ts](file://assets/script/Main.ts)
|
||
|
||
## 核心配置项解析
|
||
`config.json` 文件位于 `assets/resources/` 目录下,是整个游戏的配置中心。其 `config` 对象下的各个字段具有明确的用途。
|
||
|
||
```json
|
||
{
|
||
"config": {
|
||
"version": "1.0.0",
|
||
"package": "com.oops.game",
|
||
"localDataKey": "oops",
|
||
"localDataIv": "framework",
|
||
"httpServer": "http://192.168.0.150/main/",
|
||
"httpTimeout": 10000,
|
||
"frameRate": 60
|
||
}
|
||
}
|
||
```
|
||
|
||
- **`version`**: 标识当前游戏的版本号。此信息可用于版本控制、热更新判断以及与云端数据进行兼容性校验。
|
||
- **`package`**: 定义游戏的包标识符,遵循反向域名命名规则。在发布到应用商店或进行平台集成时,此标识符必须全局唯一。
|
||
- **`localDataKey`**: 本地数据加密所使用的密钥(Key)。该密钥与 `localDataIv` 一起,作为 `crypto-es` 库进行 AES 加密的参数,确保存储在用户设备上的数据安全。
|
||
- **`localDataIv`**: 本地数据加密所使用的初始化向量(Initialization Vector)。它增加了加密的随机性,即使相同的数据在不同时间加密,其密文也会不同,从而提高安全性。
|
||
- **`httpServer`**: 指定 HTTP 请求的服务器基础地址。所有通过 `oops.http` 模块发起的请求,其 URL 都会以此地址为前缀。
|
||
- **`httpTimeout`**: 设置 HTTP 请求的超时时间(单位:毫秒)。当网络请求超过此时间仍未收到响应时,将被视为失败,防止应用因网络问题而长时间无响应。
|
||
- **`frameRate`**: 定义游戏的帧率,即每秒渲染的帧数。较高的帧率(如 60)能提供更流畅的视觉体验,但对设备性能要求更高;较低的帧率(如 30)则更省电,适合性能较弱的设备。
|
||
|
||
**Section sources**
|
||
- [config.json](file://assets/resources/config.json)
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts#L50-L55)
|
||
|
||
## 本地数据加密机制
|
||
项目的本地数据加密方案基于 `crypto-es` 库实现,由 `Oops` 框架的 `StorageManager` 类进行封装。
|
||
|
||
### 设计思路
|
||
1. **依赖库**:项目通过 `package.json` 明确依赖了 `crypto-es` 库,这是一个轻量级的 JavaScript 加密库,支持 AES 等多种加密算法。
|
||
2. **框架集成**:`Oops` 框架在 `Root.ts` 的 `onLoad` 方法中,通过 `oops.config.game` 读取 `config.json` 中的 `localDataKey` 和 `localDataIv`,并调用 `oops.storage.init()` 方法进行初始化。
|
||
3. **条件加密**:根据 `Oops` 框架的设计,在开发调试模式(`DEBUG` 为 `true`)下,数据以明文形式存储,便于开发者调试。而在发布模式下,数据会自动使用 AES 算法进行加密后存储。
|
||
|
||
### 加密实现
|
||
`StorageManager` 类在初始化后,会拦截所有对 `sys.localStorage` 的读写操作。当调用 `set` 方法存储数据时,它会:
|
||
1. 将 JavaScript 对象序列化为 JSON 字符串。
|
||
2. 使用 `localDataKey` 作为密钥,`localDataIv` 作为初始化向量,通过 `crypto-es` 的 AES 算法对字符串进行加密。
|
||
3. 将加密后的密文(通常为 Base64 编码)存储到 `localStorage` 中。
|
||
|
||
当调用 `get` 方法读取数据时,流程则相反:
|
||
1. 从 `localStorage` 中读取密文。
|
||
2. 使用相同的密钥和初始化向量进行解密。
|
||
3. 将解密后的明文 JSON 字符串反序列化为 JavaScript 对象并返回。
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[调用 oops.storage.set(key, value)] --> B[序列化为JSON]
|
||
B --> C[使用localDataKey和localDataIv进行AES加密]
|
||
C --> D[Base64编码]
|
||
D --> E[存入localStorage]
|
||
F[调用 oops.storage.get(key)] --> G[从localStorage读取]
|
||
G --> H[Base64解码]
|
||
H --> I[使用localDataKey和localDataIv进行AES解密]
|
||
I --> J[反序列化为对象]
|
||
J --> K[返回数据]
|
||
```
|
||
|
||
**Diagram sources**
|
||
- [config.json](file://assets/resources/config.json)
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts#L55)
|
||
- [StorageManager.ts](file://extensions/oops-plugin-framework/assets/core/common/storage/StorageManager.ts)
|
||
|
||
**Section sources**
|
||
- [package.json](file://package.json)
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts#L55)
|
||
- [Oops.ts](file://extensions/oops-plugin-framework/assets/core/Oops.ts#L25)
|
||
|
||
## 配置加载与初始化流程
|
||
游戏的配置加载和初始化是一个有序的过程,由 `Main.ts` 和 `Root.ts` 共同驱动。
|
||
|
||
### 流程图
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Main as Main.ts
|
||
participant Root as Root.ts
|
||
participant Config as config.json
|
||
participant Storage as StorageManager
|
||
participant Initialize as Initialize.ts
|
||
Main->>Root : director.loadScene("main")
|
||
Root->>Root : onLoad()
|
||
Root->>Root : 加载config.json资源
|
||
Root->>Root : 解析JsonAsset
|
||
loop 初始化框架模块
|
||
Root->>Root : oops.config.game = new GameConfig(config)
|
||
Root->>Root : oops.http.server = oops.config.game.httpServer
|
||
Root->>Root : oops.http.timeout = oops.config.game.httpTimeout
|
||
Root->>Storage : oops.storage.init(localDataKey, localDataIv)
|
||
Root->>Root : game.frameRate = oops.config.game.frameRate
|
||
end
|
||
Root->>Root : enabled = true
|
||
Root->>Root : init() 和 run()
|
||
Root->>Initialize : smc.initialize = ecs.getEntity<Initialize>(Initialize)
|
||
Initialize->>Initialize : protected init()
|
||
Initialize->>Initialize : loadCustom -> loadLanguage
|
||
Initialize->>Storage : oops.storage.get("language")
|
||
alt 语言存在
|
||
Initialize->>Initialize : 使用存储的语言
|
||
else 语言不存在
|
||
Initialize->>Initialize : 默认设为"zh"
|
||
Initialize->>Storage : oops.storage.set("language", "zh")
|
||
end
|
||
Initialize->>Initialize : 继续加载公共资源和游戏数据
|
||
```
|
||
|
||
**Diagram sources**
|
||
- [Main.ts](file://assets/script/Main.ts)
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts)
|
||
- [Initialize.ts](file://assets/script/game/initialize/Initialize.ts)
|
||
|
||
**Section sources**
|
||
- [Main.ts](file://assets/script/Main.ts)
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts)
|
||
- [Initialize.ts](file://assets/script/game/initialize/Initialize.ts)
|
||
|
||
## 本地缓存与云端数据策略
|
||
项目采用了一套混合数据存储策略,结合了本地缓存和云端同步,以平衡性能、用户体验和数据安全。
|
||
|
||
### 优先级策略
|
||
1. **本地优先(读取)**:游戏启动时,会优先从本地存储中读取用户偏好设置,如语言选择 (`oops.storage.get("language")`)。这确保了即使在离线状态下,用户也能获得个性化的体验。
|
||
2. **云端优先(写入与同步)**:对于核心的游戏进度数据(如金币、英雄信息、出战阵容),项目优先使用云端存储。在微信客户端中,通过 `WxCloudApi` 与微信云函数交互,实现数据的持久化和跨设备同步。
|
||
|
||
### 数据同步流程
|
||
- **初始化同步**:在 `Initialize.ts` 的 `loadGameDataUnified` 方法中,程序首先判断是否为微信客户端。如果是,则调用 `loadFromCloud()`,通过 `WxCloudApi.login()` 获取云端数据,并用 `overrideLocalDataWithRemote()` 方法覆盖本地的 `smc` (SingletonModuleComp) 单例数据。
|
||
- **运行时同步**:在游戏运行过程中,当用户数据发生变化(如获得金币、升级英雄),会立即调用 `WxCloudApi.save()` 将最新数据保存到云端,确保数据的实时性和安全性。
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[游戏启动] --> B{是否为微信客户端?}
|
||
B -- 是 --> C[调用 WxCloudApi.login()]
|
||
C --> D{登录成功?}
|
||
D -- 是 --> E[获取云端数据]
|
||
E --> F[覆盖本地 smc 数据]
|
||
D -- 否 --> G[使用本地调试数据]
|
||
B -- 否 --> G
|
||
F --> H[进入游戏]
|
||
G --> H
|
||
```
|
||
|
||
**Diagram sources**
|
||
- [Initialize.ts](file://assets/script/game/initialize/Initialize.ts#L67-L105)
|
||
- [SingletonModuleComp.ts](file://assets/script/game/common/SingletonModuleComp.ts#L81-L121)
|
||
- [WxCloudApi.ts](file://assets/script/game/wx_clound_client_api/WxCloudApi.ts)
|
||
|
||
**Section sources**
|
||
- [Initialize.ts](file://assets/script/game/initialize/Initialize.ts)
|
||
- [SingletonModuleComp.ts](file://assets/script/game/common/SingletonModuleComp.ts)
|
||
- [WxCloudApi.ts](file://assets/script/game/wx_clound_client_api/WxCloudApi.ts)
|
||
|
||
## 性能与网络配置
|
||
除了数据存储,`config.json` 中的配置也直接影响游戏的性能和网络行为。
|
||
|
||
- **`frameRate` 对性能的影响**:`frameRate` 设置为 60,意味着游戏引擎会尽量每秒更新和渲染 60 帧。这能提供非常流畅的动画效果,但会持续占用较高的 CPU 和 GPU 资源,可能导致设备发热和耗电加快。开发者需要在流畅度和性能消耗之间做出权衡,对于性能要求不高的场景,可以考虑降低此值。
|
||
- **`httpServer` 与 `httpTimeout` 的用途**:`httpServer` 定义了所有 HTTP 请求的根地址,实现了请求地址的集中管理,便于在开发、测试和生产环境之间切换。`httpTimeout` 是一个关键的容错配置,它防止了因网络延迟或服务器无响应而导致的 UI 卡死。当请求超时后,应用可以捕获错误并给出友好的提示,提升用户体验。
|
||
|
||
**Section sources**
|
||
- [Root.ts](file://extensions/oops-plugin-framework/assets/core/Root.ts#L54-L55)
|
||
- [config.json](file://assets/resources/config.json)
|
||
|
||
## 故障处理机制
|
||
项目在数据处理和网络通信方面设计了多层次的容错机制。
|
||
|
||
- **本地存储容错**:在 `Initialize.ts` 中读取语言设置时,代码明确检查了 `oops.storage.get("language")` 的返回值是否为 `null` 或空字符串。如果不存在,则使用默认值 `"zh"` 并将其写回存储。这是一种典型的“降级”策略,确保了关键配置的可用性。
|
||
- **云端数据容错**:`loadFromCloud` 和 `loadFromLocalDebug` 方法都使用了 `try-catch` 语句包裹。当云端登录或数据获取失败时,程序会捕获异常,记录错误日志,并尝试使用本地调试数据作为后备方案,避免游戏因数据加载失败而无法启动。
|
||
- **网络请求容错**:`httpTimeout` 配置本身就是一种网络容错。此外,`WxCloudApi` 的调用通常会检查返回的 `code` 字段(如 `200` 表示成功),并根据不同的错误码(如 `-3` 参数错误,`-6` 资源不足)执行相应的处理逻辑,例如提示用户或回滚操作。
|
||
|
||
**Section sources**
|
||
- [Initialize.ts](file://assets/script/game/initialize/Initialize.ts)
|
||
- [WxCloudApi.ts](file://assets/script/game/wx_clound_client_api/WxCloudApi.ts)
|
||
|
||
## 结论
|
||
该项目的本地存储机制设计完善,通过 `config.json` 实现了配置的集中化管理。利用 `Oops` 框架和 `crypto-es` 库,实现了安全的本地数据加密。项目采用了“本地缓存 + 云端同步”的混合策略,既保证了离线可用性和启动速度,又确保了核心数据的安全与跨设备同步。整个初始化流程清晰,配置项的加载和应用时机合理,并且在各个环节都考虑了容错处理,构建了一个健壮、可靠的游戏数据管理方案。 |