Extra2D/docs/DataStore.md

14 KiB
Raw Blame History

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 - 用户IDAccount 类型需要,为空则自动获取当前用户)
  • 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 &section,
    const std::string &key,
    const std::string &defaultValue = ""
);

获取字符串值。

参数:

  • section - 节名称
  • key - 键名称
  • defaultValue - 默认值(键不存在时返回)

返回值:

  • 字符串值

getInt

int getInt(
    const std::string &section,
    const std::string &key,
    int defaultValue = 0
);

获取整数值。


getFloat

float getFloat(
    const std::string &section,
    const std::string &key,
    float defaultValue = 0.0f
);

获取浮点数值。


getBool

bool getBool(
    const std::string &section,
    const std::string &key,
    bool defaultValue = false
);

获取布尔值。

说明:

  • 支持 "true"/"false"、"yes"/"no"、"1"/"0" 等格式

setString

void setString(
    const std::string &section,
    const std::string &key,
    const std::string &value
);

设置字符串值。

说明:

  • 自动标记为脏数据
  • 事务外自动保存

setInt

void setInt(
    const std::string &section,
    const std::string &key,
    int value
);

设置整数值。


setFloat

void setFloat(
    const std::string &section,
    const std::string &key,
    float value
);

设置浮点数值。


setBool

void setBool(
    const std::string &section,
    const std::string &key,
    bool value
);

设置布尔值。


removeKey

void removeKey(const std::string &section, const std::string &key);

删除键。


removeSection

void removeSection(const std::string &section);

删除整个 section。


hasKey

bool hasKey(const std::string &section, const std::string &key);

检查键是否存在。


hasSection

bool hasSection(const std::string &section);

检查 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 &section) 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 平台

  1. 必须提交存档:修改存档数据后,必须调用 commitSaveData()saveToSave(),否则更改不会持久化

  2. 用户ID管理

    • Account 类型存档需要有效的用户ID
    • 可以使用 getCurrentUserId() 自动获取当前用户
    • 公共存档使用 Common 类型不需要用户ID
  3. 存档挂载

    • 每个挂载点需要唯一的名称
    • 应用退出时会自动卸载,但建议显式调用 unmountSaveData()
  4. 存档空间

    • 注意存档空间限制
    • 大数据(如截图)建议使用 Cache 类型

通用

  1. 事务使用

    • 批量修改时使用事务提高性能
    • 事务中的错误可以通过 rollback() 恢复
  2. 自动保存

    • 事务外每次 set 操作都会触发自动保存
    • 频繁修改建议使用事务批量处理
  3. 文件格式

    • 使用标准 INI 格式
    • 支持注释(以 ;# 开头)
    • Section 和 Key 不区分大小写

相关文件