14 KiB
14 KiB
DataStore 数据持久化系统
概述
DataStore 是 Easy2D 引擎的数据持久化类,提供 INI 文件格式的数据存储功能,并支持 Nintendo Switch 平台的官方存档系统。
特性
- ✅ INI 文件格式读写
- ✅ Nintendo Switch 官方存档系统支持
- ✅ 多用户存档隔离
- ✅ 事务支持(批量操作 + 回滚)
- ✅ 自动保存和脏数据检测
- ✅ 跨平台兼容(Switch/PC)
快速开始
基本使用
#include <extra2d/extra2d.h>
using namespace extra2d;
// 创建 DataStore 实例
DataStore data;
// 加载数据文件
data.load("config.ini");
// 读取数据
int level = data.getInt("Player", "Level", 1);
std::string name = data.getString("Player", "Name", "Unknown");
// 写入数据
data.setInt("Player", "Level", 10);
data.setString("Player", "Name", "Hero");
// 保存到文件
data.save("config.ini");
Switch 存档系统使用
DataStore saveData;
// 挂载存档(自动获取当前用户)
if (saveData.mountSaveData(SaveDataType::Account)) {
// 从存档加载
saveData.loadFromSave("savegame.ini");
// 修改数据
saveData.setInt("Progress", "Level", 5);
saveData.setInt("Progress", "Score", 1000);
// 保存到存档(自动提交)
saveData.saveToSave("savegame.ini");
// 卸载存档
saveData.unmountSaveData();
}
API 参考
枚举类型
SaveDataType
存档数据类型枚举:
| 值 | 说明 |
|---|---|
SaveDataType::Account |
用户存档,与特定用户账户关联 |
SaveDataType::Common |
公共存档,所有用户共享 |
SaveDataType::Cache |
缓存数据,可被系统清理 |
SaveDataType::Device |
设备存档,与设备绑定 |
SaveDataType::Temporary |
临时数据,应用退出后清理 |
UserId
用户ID结构体:
struct UserId {
uint64_t uid[2]; // 用户唯一标识
bool isValid() const; // 检查ID是否有效
};
构造函数
DataStore();
~DataStore();
说明:
- 析构时会自动提交未保存的事务
- 如果存档已挂载,会自动卸载
文件操作
load
bool load(const std::string &filename);
加载 INI 文件。
参数:
filename- 文件路径
返回值:
true- 加载成功false- 加载失败
示例:
if (!data.load("config.ini")) {
E2D_LOG_WARN("配置文件不存在,将创建新文件");
}
save
bool save(const std::string &filename = "");
保存到 INI 文件。
参数:
filename- 文件路径,为空则使用上次加载的文件名
返回值:
true- 保存成功false- 保存失败
说明:
- 如果在事务中,只标记为脏数据,不实际写入
- 事务外自动保存(当设置数据时)
Switch 存档系统
mountSaveData
bool mountSaveData(
SaveDataType type = SaveDataType::Account,
const UserId &userId = UserId(),
const std::string &mountName = "save"
);
挂载 Switch 存档数据。
参数:
type- 存档类型userId- 用户ID(Account 类型需要,为空则自动获取当前用户)mountName- 挂载点名称
返回值:
true- 挂载成功false- 挂载失败
示例:
// 挂载当前用户的存档
if (data.mountSaveData(SaveDataType::Account)) {
// 存档已挂载,路径为 "save:/"
}
// 挂载公共存档
if (data.mountSaveData(SaveDataType::Common, UserId(), "common")) {
// 公共存档已挂载,路径为 "common:/"
}
unmountSaveData
void unmountSaveData(const std::string &mountName = "save");
卸载存档挂载。
参数:
mountName- 挂载点名称
说明:
- 卸载前会自动提交未保存的更改
commitSaveData
bool commitSaveData(const std::string &mountName = "save");
提交存档更改。
⚠️ 重要: 修改存档数据后必须调用此方法,否则更改不会持久化!
参数:
mountName- 挂载点名称
返回值:
true- 提交成功false- 提交失败
示例:
data.setInt("Game", "Score", 100);
data.save();
data.commitSaveData(); // 必须提交!
isSaveDataMounted
bool isSaveDataMounted() const;
检查存档是否已挂载。
返回值:
true- 已挂载false- 未挂载
getSaveDataPath
std::string getSaveDataPath(const std::string &path = "") const;
获取挂载点路径。
参数:
path- 子路径
返回值:
- 完整路径(如 "save:/data/config.ini")
示例:
std::string fullPath = data.getSaveDataPath("config/settings.ini");
// 返回: "save:/config/settings.ini"
用户账户管理
getCurrentUserId
static UserId getCurrentUserId();
获取当前预选用户ID。
返回值:
- 用户ID(无效时返回空ID)
示例:
UserId user = DataStore::getCurrentUserId();
if (user.isValid()) {
E2D_LOG_INFO("当前用户: 0x{:X}{:X}", user.uid[1], user.uid[0]);
}
setDefaultUserId / getDefaultUserId
void setDefaultUserId(const UserId &userId);
UserId getDefaultUserId() const;
设置/获取默认用户ID。
说明:
- 用于多用户场景下的默认用户选择
数据读写
getString
std::string getString(
const std::string §ion,
const std::string &key,
const std::string &defaultValue = ""
);
获取字符串值。
参数:
section- 节名称key- 键名称defaultValue- 默认值(键不存在时返回)
返回值:
- 字符串值
getInt
int getInt(
const std::string §ion,
const std::string &key,
int defaultValue = 0
);
获取整数值。
getFloat
float getFloat(
const std::string §ion,
const std::string &key,
float defaultValue = 0.0f
);
获取浮点数值。
getBool
bool getBool(
const std::string §ion,
const std::string &key,
bool defaultValue = false
);
获取布尔值。
说明:
- 支持 "true"/"false"、"yes"/"no"、"1"/"0" 等格式
setString
void setString(
const std::string §ion,
const std::string &key,
const std::string &value
);
设置字符串值。
说明:
- 自动标记为脏数据
- 事务外自动保存
setInt
void setInt(
const std::string §ion,
const std::string &key,
int value
);
设置整数值。
setFloat
void setFloat(
const std::string §ion,
const std::string &key,
float value
);
设置浮点数值。
setBool
void setBool(
const std::string §ion,
const std::string &key,
bool value
);
设置布尔值。
removeKey
void removeKey(const std::string §ion, const std::string &key);
删除键。
removeSection
void removeSection(const std::string §ion);
删除整个 section。
hasKey
bool hasKey(const std::string §ion, const std::string &key);
检查键是否存在。
hasSection
bool hasSection(const std::string §ion);
检查 section 是否存在。
clear
void clear();
清除所有数据。
事务支持
beginTransaction
void beginTransaction();
开始事务。
说明:
- 事务中的修改不会立即写入文件
- 支持批量操作,提高性能
- 事务可以回滚
示例:
data.beginTransaction();
// 批量修改
data.setInt("Player", "Level", 10);
data.setInt("Player", "Exp", 1000);
data.setString("Player", "Title", "Knight");
// 提交事务
data.commit();
commit
bool commit();
提交事务。
返回值:
true- 提交成功false- 提交失败
说明:
- 写入文件并提交存档(如果已挂载)
- 事务结束后自动保存
rollback
void rollback();
回滚事务。
说明:
- 放弃事务中的所有修改
- 重新加载文件恢复数据
示例:
data.beginTransaction();
data.setInt("Test", "Value", 999);
// 放弃修改
data.rollback();
// Value 恢复为原来的值
isInTransaction
bool isInTransaction() const;
检查是否在事务中。
工具方法
getAllSections
std::vector<std::string> getAllSections() const;
获取所有 section 名称。
返回值:
- section 名称列表
getAllKeys
std::vector<std::string> getAllKeys(const std::string §ion) const;
获取指定 section 的所有 key。
参数:
section- section 名称
返回值:
- key 名称列表
loadFromSave
bool loadFromSave(const std::string &path);
从存档加载(自动处理挂载路径)。
参数:
path- 存档内文件路径
返回值:
true- 加载成功false- 加载失败(存档未挂载或文件不存在)
示例:
if (data.loadFromSave("savegame.ini")) {
// 加载成功
}
saveToSave
bool saveToSave(const std::string &path);
保存到存档(自动处理挂载路径和提交)。
参数:
path- 存档内文件路径
返回值:
true- 保存成功false- 保存失败
说明:
- 自动调用
commitSaveData()提交更改
完整示例
游戏存档管理
#include <extra2d/extra2d.h>
using namespace extra2d;
class SaveManager {
private:
DataStore saveData_;
bool mounted_ = false;
public:
bool initialize() {
// 挂载用户存档
mounted_ = saveData_.mountSaveData(SaveDataType::Account);
if (!mounted_) {
E2D_LOG_ERROR("存档挂载失败");
return false;
}
// 加载存档
if (!saveData_.loadFromSave("game_save.ini")) {
E2D_LOG_INFO("存档不存在,创建新存档");
createNewSave();
}
return true;
}
void shutdown() {
if (mounted_) {
saveData_.unmountSaveData();
mounted_ = false;
}
}
void createNewSave() {
saveData_.beginTransaction();
saveData_.setString("Player", "Name", "Player1");
saveData_.setInt("Player", "Level", 1);
saveData_.setInt("Player", "Exp", 0);
saveData_.setInt("Progress", "Chapter", 1);
saveData_.setInt("Settings", "Difficulty", 1);
saveData_.commit();
saveData_.saveToSave("game_save.ini");
}
void saveGame(const PlayerData &player) {
saveData_.beginTransaction();
saveData_.setInt("Player", "Level", player.level);
saveData_.setInt("Player", "Exp", player.exp);
saveData_.setInt("Player", "Health", player.health);
saveData_.setInt("Player", "Mana", player.mana);
saveData_.setInt("Progress", "Chapter", player.chapter);
saveData_.setString("Progress", "Checkpoint", player.checkpoint);
if (saveData_.commit()) {
saveData_.saveToSave("game_save.ini");
E2D_LOG_INFO("游戏已保存");
} else {
E2D_LOG_ERROR("保存失败");
}
}
void loadGame(PlayerData &player) {
player.level = saveData_.getInt("Player", "Level", 1);
player.exp = saveData_.getInt("Player", "Exp", 0);
player.health = saveData_.getInt("Player", "Health", 100);
player.mana = saveData_.getInt("Player", "Mana", 50);
player.chapter = saveData_.getInt("Progress", "Chapter", 1);
player.checkpoint = saveData_.getString("Progress", "Checkpoint", "start");
}
bool hasSaveFile() {
return saveData_.hasSection("Player");
}
};
设置管理
class SettingsManager {
private:
DataStore settings_;
public:
void load() {
// PC 平台使用普通文件
#ifndef __SWITCH__
settings_.load("settings.ini");
#else
// Switch 平台使用存档
if (settings_.mountSaveData(SaveDataType::Common)) {
settings_.loadFromSave("settings.ini");
}
#endif
}
void save() {
#ifndef __SWITCH__
settings_.save("settings.ini");
#else
settings_.saveToSave("settings.ini");
settings_.unmountSaveData();
#endif
}
int getVolume() {
return settings_.getInt("Audio", "Volume", 80);
}
void setVolume(int volume) {
settings_.setInt("Audio", "Volume", volume);
}
bool isFullscreen() {
return settings_.getBool("Video", "Fullscreen", false);
}
void setFullscreen(bool fullscreen) {
settings_.setBool("Video", "Fullscreen", fullscreen);
}
};
注意事项
Switch 平台
-
必须提交存档:修改存档数据后,必须调用
commitSaveData()或saveToSave(),否则更改不会持久化 -
用户ID管理:
Account类型存档需要有效的用户ID- 可以使用
getCurrentUserId()自动获取当前用户 - 公共存档使用
Common类型,不需要用户ID
-
存档挂载:
- 每个挂载点需要唯一的名称
- 应用退出时会自动卸载,但建议显式调用
unmountSaveData()
-
存档空间:
- 注意存档空间限制
- 大数据(如截图)建议使用
Cache类型
通用
-
事务使用:
- 批量修改时使用事务提高性能
- 事务中的错误可以通过
rollback()恢复
-
自动保存:
- 事务外每次
set操作都会触发自动保存 - 频繁修改建议使用事务批量处理
- 事务外每次
-
文件格式:
- 使用标准 INI 格式
- 支持注释(以
;或#开头) - Section 和 Key 不区分大小写