refactor: 移除事件系统和音频系统相关代码
移除不再使用的事件队列(EventQueue)、事件分发器(EventDispatcher)、音频引擎(AudioEngine)和声音(Sound)相关代码 删除utils/data.h数据存储模块 清理相关头文件引用和成员变量
This commit is contained in:
parent
ea081b9dd3
commit
24b86b4916
|
|
@ -9,10 +9,7 @@ namespace extra2d {
|
|||
|
||||
// 前向声明
|
||||
class Input;
|
||||
class AudioEngine;
|
||||
class TimerManager;
|
||||
class EventQueue;
|
||||
class EventDispatcher;
|
||||
|
||||
// ============================================================================
|
||||
// Application 配置
|
||||
|
|
@ -56,10 +53,7 @@ public:
|
|||
|
||||
Window &window() { return *window_; }
|
||||
Input &input();
|
||||
AudioEngine &audio();
|
||||
TimerManager &timers();
|
||||
EventQueue &eventQueue();
|
||||
EventDispatcher &eventDispatcher();
|
||||
|
||||
float deltaTime() const { return deltaTime_; }
|
||||
float totalTime() const { return totalTime_; }
|
||||
|
|
@ -78,8 +72,6 @@ private:
|
|||
|
||||
UniquePtr<Window> window_;
|
||||
UniquePtr<TimerManager> timerManager_;
|
||||
UniquePtr<EventQueue> eventQueue_;
|
||||
UniquePtr<EventDispatcher> eventDispatcher_;
|
||||
|
||||
bool initialized_ = false;
|
||||
bool running_ = false;
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/intrusive_ptr.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Sound;
|
||||
|
||||
class AudioEngine {
|
||||
public:
|
||||
static AudioEngine &getInstance();
|
||||
|
||||
AudioEngine(const AudioEngine &) = delete;
|
||||
AudioEngine &operator=(const AudioEngine &) = delete;
|
||||
AudioEngine(AudioEngine &&) = delete;
|
||||
AudioEngine &operator=(AudioEngine &&) = delete;
|
||||
|
||||
bool initialize();
|
||||
void shutdown();
|
||||
|
||||
IntrusivePtr<Sound> loadSound(const std::string &filePath);
|
||||
IntrusivePtr<Sound> loadSound(const std::string &name,
|
||||
const std::string &filePath);
|
||||
|
||||
IntrusivePtr<Sound> getSound(const std::string &name);
|
||||
void unloadSound(const std::string &name);
|
||||
void unloadAllSounds();
|
||||
|
||||
void setMasterVolume(float volume);
|
||||
float getMasterVolume() const;
|
||||
|
||||
void pauseAll();
|
||||
void resumeAll();
|
||||
void stopAll();
|
||||
|
||||
private:
|
||||
AudioEngine() = default;
|
||||
~AudioEngine();
|
||||
|
||||
std::unordered_map<std::string, IntrusivePtr<Sound>> sounds_;
|
||||
float masterVolume_ = 1.0f;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/ref_counted.h>
|
||||
#include <string>
|
||||
|
||||
struct Mix_Chunk;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class AudioEngine;
|
||||
|
||||
class Sound : public RefCounted {
|
||||
public:
|
||||
~Sound();
|
||||
|
||||
Sound(const Sound&) = delete;
|
||||
Sound& operator=(const Sound&) = delete;
|
||||
|
||||
bool play();
|
||||
void pause();
|
||||
void resume();
|
||||
void stop();
|
||||
|
||||
bool isPlaying() const;
|
||||
bool isPaused() const;
|
||||
|
||||
void setVolume(float volume);
|
||||
float getVolume() const { return volume_; }
|
||||
|
||||
void setLooping(bool looping);
|
||||
bool isLooping() const { return looping_; }
|
||||
|
||||
void setPitch(float pitch);
|
||||
float getPitch() const { return pitch_; }
|
||||
|
||||
float getDuration() const;
|
||||
float getCursor() const;
|
||||
void setCursor(float seconds);
|
||||
|
||||
const std::string& getFilePath() const { return filePath_; }
|
||||
const std::string& name() const { return name_; }
|
||||
|
||||
private:
|
||||
friend class AudioEngine;
|
||||
|
||||
Sound(const std::string& name, const std::string& filePath, Mix_Chunk* chunk);
|
||||
|
||||
std::string name_;
|
||||
std::string filePath_;
|
||||
Mix_Chunk* chunk_ = nullptr;
|
||||
int channel_ = -1; // SDL_mixer 分配的通道,-1 表示未播放
|
||||
float volume_ = 1.0f;
|
||||
float pitch_ = 1.0f;
|
||||
bool looping_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <core/types.h>
|
||||
#include <core/vec2.h>
|
||||
#include <variant>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件类型枚举
|
||||
// ============================================================================
|
||||
enum class EventType {
|
||||
None = 0,
|
||||
|
||||
// 窗口事件
|
||||
WindowClose,
|
||||
WindowResize,
|
||||
WindowFocus,
|
||||
WindowLostFocus,
|
||||
WindowMoved,
|
||||
|
||||
// 键盘事件
|
||||
KeyPressed,
|
||||
KeyReleased,
|
||||
KeyRepeat,
|
||||
|
||||
// 鼠标事件
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseMoved,
|
||||
MouseScrolled,
|
||||
|
||||
// UI 事件
|
||||
UIHoverEnter,
|
||||
UIHoverExit,
|
||||
UIPressed,
|
||||
UIReleased,
|
||||
UIClicked,
|
||||
|
||||
// 游戏手柄事件
|
||||
GamepadConnected,
|
||||
GamepadDisconnected,
|
||||
GamepadButtonPressed,
|
||||
GamepadButtonReleased,
|
||||
GamepadAxisMoved,
|
||||
|
||||
// 触摸事件 (移动端)
|
||||
TouchBegan,
|
||||
TouchMoved,
|
||||
TouchEnded,
|
||||
TouchCancelled,
|
||||
|
||||
// 自定义事件
|
||||
Custom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 键盘事件数据
|
||||
// ============================================================================
|
||||
struct KeyEvent {
|
||||
int keyCode;
|
||||
int scancode;
|
||||
int mods; // 修饰键 (Shift, Ctrl, Alt, etc.)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标事件数据
|
||||
// ============================================================================
|
||||
struct MouseButtonEvent {
|
||||
int button;
|
||||
int mods;
|
||||
Vec2 position;
|
||||
};
|
||||
|
||||
struct MouseMoveEvent {
|
||||
Vec2 position;
|
||||
Vec2 delta;
|
||||
};
|
||||
|
||||
struct MouseScrollEvent {
|
||||
Vec2 offset;
|
||||
Vec2 position;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 窗口事件数据
|
||||
// ============================================================================
|
||||
struct WindowResizeEvent {
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct WindowMoveEvent {
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄事件数据
|
||||
// ============================================================================
|
||||
struct GamepadButtonEvent {
|
||||
int gamepadId;
|
||||
int button;
|
||||
};
|
||||
|
||||
struct GamepadAxisEvent {
|
||||
int gamepadId;
|
||||
int axis;
|
||||
float value;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 触摸事件数据
|
||||
// ============================================================================
|
||||
struct TouchEvent {
|
||||
int touchId;
|
||||
Vec2 position;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义事件数据
|
||||
// ============================================================================
|
||||
struct CustomEvent {
|
||||
uint32_t id;
|
||||
void *data;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 事件结构
|
||||
// ============================================================================
|
||||
struct Event {
|
||||
EventType type = EventType::None;
|
||||
double timestamp = 0.0;
|
||||
bool handled = false;
|
||||
|
||||
// 事件数据联合体
|
||||
std::variant<std::monostate, KeyEvent, MouseButtonEvent, MouseMoveEvent,
|
||||
MouseScrollEvent, WindowResizeEvent, WindowMoveEvent,
|
||||
GamepadButtonEvent, GamepadAxisEvent, TouchEvent, CustomEvent>
|
||||
data;
|
||||
|
||||
// 便捷访问方法
|
||||
bool isWindowEvent() const {
|
||||
return type == EventType::WindowClose || type == EventType::WindowResize ||
|
||||
type == EventType::WindowFocus ||
|
||||
type == EventType::WindowLostFocus || type == EventType::WindowMoved;
|
||||
}
|
||||
|
||||
bool isKeyboardEvent() const {
|
||||
return type == EventType::KeyPressed || type == EventType::KeyReleased ||
|
||||
type == EventType::KeyRepeat;
|
||||
}
|
||||
|
||||
bool isMouseEvent() const {
|
||||
return type == EventType::MouseButtonPressed ||
|
||||
type == EventType::MouseButtonReleased ||
|
||||
type == EventType::MouseMoved || type == EventType::MouseScrolled;
|
||||
}
|
||||
|
||||
// 静态工厂方法
|
||||
static Event createWindowResize(int width, int height);
|
||||
static Event createWindowClose();
|
||||
static Event createKeyPress(int keyCode, int scancode, int mods);
|
||||
static Event createKeyRelease(int keyCode, int scancode, int mods);
|
||||
static Event createMouseButtonPress(int button, int mods, const Vec2 &pos);
|
||||
static Event createMouseButtonRelease(int button, int mods, const Vec2 &pos);
|
||||
static Event createMouseMove(const Vec2 &pos, const Vec2 &delta);
|
||||
static Event createMouseScroll(const Vec2 &offset, const Vec2 &pos);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <event/event.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件监听器 ID
|
||||
// ============================================================================
|
||||
using ListenerId = uint64_t;
|
||||
|
||||
// ============================================================================
|
||||
// 事件分发器
|
||||
// ============================================================================
|
||||
class EventDispatcher {
|
||||
public:
|
||||
using EventCallback = std::function<void(Event &)>;
|
||||
|
||||
EventDispatcher();
|
||||
~EventDispatcher() = default;
|
||||
|
||||
// 添加监听器
|
||||
ListenerId addListener(EventType type, EventCallback callback);
|
||||
|
||||
// 移除监听器
|
||||
void removeListener(ListenerId id);
|
||||
void removeAllListeners(EventType type);
|
||||
void removeAllListeners();
|
||||
|
||||
// 分发事件
|
||||
void dispatch(Event &event);
|
||||
void dispatch(const Event &event);
|
||||
|
||||
// 处理事件队列
|
||||
void processQueue(class EventQueue &queue);
|
||||
|
||||
// 统计
|
||||
size_t getListenerCount(EventType type) const;
|
||||
size_t getTotalListenerCount() const;
|
||||
|
||||
private:
|
||||
struct Listener {
|
||||
ListenerId id;
|
||||
EventType type;
|
||||
EventCallback callback;
|
||||
};
|
||||
|
||||
std::unordered_map<EventType, std::vector<Listener>> listeners_;
|
||||
ListenerId nextId_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <event/event.h>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件队列 - 线程安全的事件队列
|
||||
// ============================================================================
|
||||
class EventQueue {
|
||||
public:
|
||||
EventQueue();
|
||||
~EventQueue() = default;
|
||||
|
||||
// 添加事件到队列
|
||||
void push(const Event &event);
|
||||
void push(Event &&event);
|
||||
|
||||
// 从队列取出事件
|
||||
bool poll(Event &event);
|
||||
|
||||
// 查看队列头部事件(不移除)
|
||||
bool peek(Event &event) const;
|
||||
|
||||
// 清空队列
|
||||
void clear();
|
||||
|
||||
// 队列状态
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
|
||||
private:
|
||||
std::queue<Event> queue_;
|
||||
mutable std::mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -16,18 +16,7 @@
|
|||
#include <platform/input.h>
|
||||
#include <platform/window.h>
|
||||
|
||||
// Event
|
||||
#include <event/event.h>
|
||||
#include <event/event_dispatcher.h>
|
||||
#include <event/event_queue.h>
|
||||
#include <platform/input_codes.h>
|
||||
|
||||
// Audio
|
||||
#include <audio/audio_engine.h>
|
||||
#include <audio/sound.h>
|
||||
|
||||
// Utils
|
||||
#include <utils/data.h>
|
||||
#include <utils/logger.h>
|
||||
#include <utils/random.h>
|
||||
#include <utils/timer.h>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@
|
|||
namespace extra2d {
|
||||
|
||||
// 前向声明
|
||||
class EventQueue;
|
||||
class Input;
|
||||
|
||||
// ============================================================================
|
||||
|
|
@ -102,10 +101,6 @@ public:
|
|||
void setUserData(void *data) { userData_ = data; }
|
||||
void *getUserData() const { return userData_; }
|
||||
|
||||
// 事件队列
|
||||
void setEventQueue(EventQueue *queue) { eventQueue_ = queue; }
|
||||
EventQueue *getEventQueue() const { return eventQueue_; }
|
||||
|
||||
// 获取输入管理器
|
||||
Input *getInput() const { return input_.get(); }
|
||||
|
||||
|
|
@ -142,7 +137,6 @@ private:
|
|||
float contentScaleY_;
|
||||
bool enableDpiScale_;
|
||||
void *userData_;
|
||||
EventQueue *eventQueue_;
|
||||
UniquePtr<Input> input_;
|
||||
|
||||
ResizeCallback resizeCallback_;
|
||||
|
|
|
|||
|
|
@ -1,217 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <core/types.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 存档类型枚举
|
||||
// ============================================================================
|
||||
enum class SaveDataType {
|
||||
Account, // 用户存档(与特定用户关联)
|
||||
Common, // 公共存档(所有用户共享)
|
||||
Cache, // 缓存数据(可删除)
|
||||
Device, // 设备存档
|
||||
Temporary, // 临时数据
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 用户ID结构(封装 Switch AccountUid)
|
||||
// ============================================================================
|
||||
struct UserId {
|
||||
uint64_t uid[2] = {0, 0};
|
||||
|
||||
bool isValid() const { return uid[0] != 0 || uid[1] != 0; }
|
||||
bool operator==(const UserId &other) const {
|
||||
return uid[0] == other.uid[0] && uid[1] == other.uid[1];
|
||||
}
|
||||
bool operator!=(const UserId &other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// DataStore 类 - 数据持久化(支持 Switch 存档系统)
|
||||
// ============================================================================
|
||||
class DataStore {
|
||||
public:
|
||||
DataStore();
|
||||
~DataStore();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 文件操作
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 加载 INI 文件
|
||||
bool load(const std::string &filename);
|
||||
|
||||
/// 保存到 INI 文件
|
||||
bool save(const std::string &filename);
|
||||
|
||||
/// 获取当前文件名
|
||||
const std::string &getFilename() const { return filename_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Switch 存档系统支持
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 挂载 Switch 存档数据
|
||||
* @param type 存档类型
|
||||
* @param userId 用户ID(Account 类型需要)
|
||||
* @param mountName 挂载点名称(默认 "save")
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool mountSaveData(SaveDataType type = SaveDataType::Account,
|
||||
const UserId &userId = UserId(),
|
||||
const std::string &mountName = "save");
|
||||
|
||||
/**
|
||||
* @brief 卸载存档挂载
|
||||
* @param mountName 挂载点名称
|
||||
*/
|
||||
void unmountSaveData(const std::string &mountName = "save");
|
||||
|
||||
/**
|
||||
* @brief 提交存档更改(重要:修改后必须调用)
|
||||
* @param mountName 挂载点名称
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool commitSaveData(const std::string &mountName = "save");
|
||||
|
||||
/**
|
||||
* @brief 检查存档是否已挂载
|
||||
*/
|
||||
bool isSaveDataMounted() const { return saveDataMounted_; }
|
||||
|
||||
/**
|
||||
* @brief 获取挂载点路径
|
||||
*/
|
||||
std::string getSaveDataPath(const std::string &path = "") const;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 用户账户管理
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 获取当前预选用户ID
|
||||
* @return 用户ID(无效时返回空ID)
|
||||
*/
|
||||
static UserId getCurrentUserId();
|
||||
|
||||
/**
|
||||
* @brief 设置默认用户ID
|
||||
*/
|
||||
void setDefaultUserId(const UserId &userId) { defaultUserId_ = userId; }
|
||||
|
||||
/**
|
||||
* @brief 获取默认用户ID
|
||||
*/
|
||||
UserId getDefaultUserId() const { return defaultUserId_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 数据读写
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 获取字符串值
|
||||
std::string getString(const std::string §ion, const std::string &key,
|
||||
const std::string &defaultValue = "");
|
||||
|
||||
/// 获取整数值
|
||||
int getInt(const std::string §ion, const std::string &key,
|
||||
int defaultValue = 0);
|
||||
|
||||
/// 获取浮点数值
|
||||
float getFloat(const std::string §ion, const std::string &key,
|
||||
float defaultValue = 0.0f);
|
||||
|
||||
/// 获取布尔值
|
||||
bool getBool(const std::string §ion, const std::string &key,
|
||||
bool defaultValue = false);
|
||||
|
||||
/// 设置字符串值
|
||||
void setString(const std::string §ion, const std::string &key,
|
||||
const std::string &value);
|
||||
|
||||
/// 设置整数值
|
||||
void setInt(const std::string §ion, const std::string &key, int value);
|
||||
|
||||
/// 设置浮点数值
|
||||
void setFloat(const std::string §ion, const std::string &key,
|
||||
float value);
|
||||
|
||||
/// 设置布尔值
|
||||
void setBool(const std::string §ion, const std::string &key, bool value);
|
||||
|
||||
/// 删除键
|
||||
void removeKey(const std::string §ion, const std::string &key);
|
||||
|
||||
/// 删除整个 section
|
||||
void removeSection(const std::string §ion);
|
||||
|
||||
/// 检查键是否存在
|
||||
bool hasKey(const std::string §ion, const std::string &key);
|
||||
|
||||
/// 检查 section 是否存在
|
||||
bool hasSection(const std::string §ion);
|
||||
|
||||
/// 清除所有数据
|
||||
void clear();
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 事务支持
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 开始事务(批量操作,延迟写入)
|
||||
*/
|
||||
void beginTransaction();
|
||||
|
||||
/**
|
||||
* @brief 提交事务(写入文件)
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool commit();
|
||||
|
||||
/**
|
||||
* @brief 回滚事务(放弃更改)
|
||||
*/
|
||||
void rollback();
|
||||
|
||||
/**
|
||||
* @brief 检查是否在事务中
|
||||
*/
|
||||
bool isInTransaction() const { return inTransaction_; }
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// 工具方法
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
/// 获取所有 section 名称
|
||||
std::vector<std::string> getAllSections() const;
|
||||
|
||||
/// 获取指定 section 的所有 key
|
||||
std::vector<std::string> getAllKeys(const std::string §ion) const;
|
||||
|
||||
/// 从存档加载(自动处理挂载路径)
|
||||
bool loadFromSave(const std::string &path);
|
||||
|
||||
/// 保存到存档(自动处理挂载路径和提交)
|
||||
bool saveToSave(const std::string &path);
|
||||
|
||||
private:
|
||||
class Impl;
|
||||
UniquePtr<Impl> impl_;
|
||||
std::string filename_;
|
||||
std::string mountName_;
|
||||
UserId defaultUserId_;
|
||||
bool saveDataMounted_ = false;
|
||||
bool inTransaction_ = false;
|
||||
bool dirty_ = false;
|
||||
|
||||
// 内部辅助方法
|
||||
bool internalSave(const std::string &filename);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,7 +1,4 @@
|
|||
#include <app/application.h>
|
||||
#include <audio/audio_engine.h>
|
||||
#include <event/event_dispatcher.h>
|
||||
#include <event/event_queue.h>
|
||||
#include <platform/input.h>
|
||||
#include <platform/window.h>
|
||||
#include <utils/logger.h>
|
||||
|
|
@ -102,10 +99,6 @@ bool Application::init(const AppConfig &config) {
|
|||
}
|
||||
|
||||
timerManager_ = unique<TimerManager>();
|
||||
eventQueue_ = unique<EventQueue>();
|
||||
eventDispatcher_ = unique<EventDispatcher>();
|
||||
|
||||
AudioEngine::getInstance().initialize();
|
||||
|
||||
initialized_ = true;
|
||||
running_ = true;
|
||||
|
|
@ -120,11 +113,7 @@ void Application::shutdown() {
|
|||
|
||||
E2D_LOG_INFO("Shutting down application...");
|
||||
|
||||
AudioEngine::getInstance().shutdown();
|
||||
|
||||
timerManager_.reset();
|
||||
eventQueue_.reset();
|
||||
eventDispatcher_.reset();
|
||||
|
||||
if (window_) {
|
||||
window_->destroy();
|
||||
|
|
@ -208,10 +197,6 @@ void Application::mainLoop() {
|
|||
|
||||
window_->pollEvents();
|
||||
|
||||
if (eventDispatcher_ && eventQueue_) {
|
||||
eventDispatcher_->processQueue(*eventQueue_);
|
||||
}
|
||||
|
||||
if (!paused_) {
|
||||
update();
|
||||
}
|
||||
|
|
@ -237,12 +222,6 @@ void Application::update() {
|
|||
|
||||
Input &Application::input() { return *window_->getInput(); }
|
||||
|
||||
AudioEngine &Application::audio() { return AudioEngine::getInstance(); }
|
||||
|
||||
TimerManager &Application::timers() { return *timerManager_; }
|
||||
|
||||
EventQueue &Application::eventQueue() { return *eventQueue_; }
|
||||
|
||||
EventDispatcher &Application::eventDispatcher() { return *eventDispatcher_; }
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
|
|||
|
|
@ -1,134 +0,0 @@
|
|||
#include "audio/audio_engine.h"
|
||||
#include "audio/sound.h"
|
||||
#include "utils/logger.h"
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
#include <SDL2/SDL_mixer.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
AudioEngine &AudioEngine::getInstance() {
|
||||
static AudioEngine instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
AudioEngine::~AudioEngine() { shutdown(); }
|
||||
|
||||
bool AudioEngine::initialize() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化 SDL 音频子系统
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
E2D_LOG_ERROR("Failed to initialize SDL audio: {}", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
// 打开音频设备
|
||||
if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 4096) < 0) {
|
||||
E2D_LOG_ERROR("Failed to open audio: {}", Mix_GetError());
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 分配混音通道
|
||||
Mix_AllocateChannels(32);
|
||||
|
||||
initialized_ = true;
|
||||
E2D_LOG_INFO("AudioEngine initialized successfully (SDL2_mixer)");
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioEngine::shutdown() {
|
||||
if (!initialized_) {
|
||||
return;
|
||||
}
|
||||
|
||||
unloadAllSounds();
|
||||
|
||||
Mix_CloseAudio();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
initialized_ = false;
|
||||
E2D_LOG_INFO("AudioEngine shutdown");
|
||||
}
|
||||
|
||||
IntrusivePtr<Sound> AudioEngine::loadSound(const std::string &filePath) {
|
||||
return loadSound(filePath, filePath);
|
||||
}
|
||||
|
||||
IntrusivePtr<Sound> AudioEngine::loadSound(const std::string &name,
|
||||
const std::string &filePath) {
|
||||
if (!initialized_) {
|
||||
E2D_LOG_ERROR("AudioEngine not initialized");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// 检查是否已存在
|
||||
auto it = sounds_.find(name);
|
||||
if (it != sounds_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
Mix_Chunk *chunk = Mix_LoadWAV(filePath.c_str());
|
||||
if (!chunk) {
|
||||
E2D_LOG_ERROR("Failed to load sound: {} ({})", filePath, Mix_GetError());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto sound = IntrusivePtr<Sound>(new Sound(name, filePath, chunk));
|
||||
sounds_[name] = sound;
|
||||
|
||||
E2D_LOG_DEBUG("Loaded sound: {}", filePath);
|
||||
return sound;
|
||||
}
|
||||
|
||||
IntrusivePtr<Sound> AudioEngine::getSound(const std::string &name) {
|
||||
auto it = sounds_.find(name);
|
||||
if (it != sounds_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void AudioEngine::unloadSound(const std::string &name) {
|
||||
auto it = sounds_.find(name);
|
||||
if (it != sounds_.end()) {
|
||||
sounds_.erase(it);
|
||||
E2D_LOG_DEBUG("Unloaded sound: {}", name);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioEngine::unloadAllSounds() {
|
||||
stopAll();
|
||||
sounds_.clear();
|
||||
E2D_LOG_DEBUG("Unloaded all sounds");
|
||||
}
|
||||
|
||||
void AudioEngine::setMasterVolume(float volume) {
|
||||
masterVolume_ = volume;
|
||||
int mixVol = static_cast<int>(volume * MIX_MAX_VOLUME);
|
||||
Mix_Volume(-1, mixVol); // 所有通道
|
||||
Mix_VolumeMusic(mixVol); // 音乐
|
||||
}
|
||||
|
||||
float AudioEngine::getMasterVolume() const { return masterVolume_; }
|
||||
|
||||
void AudioEngine::pauseAll() {
|
||||
Mix_Pause(-1); // 暂停所有通道
|
||||
}
|
||||
|
||||
void AudioEngine::resumeAll() {
|
||||
Mix_Resume(-1); // 恢复所有通道
|
||||
}
|
||||
|
||||
void AudioEngine::stopAll() {
|
||||
for (auto &pair : sounds_) {
|
||||
if (pair.second) {
|
||||
pair.second->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#include <SDL2/SDL_mixer.h>
|
||||
#include <audio/sound.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Sound::Sound(const std::string &name, const std::string &filePath,
|
||||
Mix_Chunk *chunk)
|
||||
: name_(name), filePath_(filePath), chunk_(chunk) {}
|
||||
|
||||
Sound::~Sound() {
|
||||
if (channel_ >= 0) {
|
||||
Mix_HaltChannel(channel_);
|
||||
channel_ = -1;
|
||||
}
|
||||
if (chunk_) {
|
||||
Mix_FreeChunk(chunk_);
|
||||
chunk_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::play() {
|
||||
if (!chunk_) {
|
||||
E2D_LOG_WARN("Sound::play() failed: chunk is null for {}", name_);
|
||||
return false;
|
||||
}
|
||||
|
||||
int loops = looping_ ? -1 : 0;
|
||||
int newChannel = Mix_PlayChannel(-1, chunk_, loops);
|
||||
|
||||
if (newChannel < 0) {
|
||||
E2D_LOG_WARN("Sound::play() failed: no free channel for {} ({})", name_, Mix_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
channel_ = newChannel;
|
||||
|
||||
int mixVol = static_cast<int>(volume_ * MIX_MAX_VOLUME);
|
||||
Mix_Volume(channel_, mixVol);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Sound::pause() {
|
||||
if (channel_ >= 0) {
|
||||
Mix_Pause(channel_);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::resume() {
|
||||
if (channel_ >= 0) {
|
||||
Mix_Resume(channel_);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::stop() {
|
||||
if (channel_ >= 0) {
|
||||
Mix_HaltChannel(channel_);
|
||||
channel_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool Sound::isPlaying() const {
|
||||
if (channel_ < 0) {
|
||||
return false;
|
||||
}
|
||||
return Mix_Playing(channel_) && !Mix_Paused(channel_);
|
||||
}
|
||||
|
||||
bool Sound::isPaused() const {
|
||||
if (channel_ < 0) {
|
||||
return false;
|
||||
}
|
||||
return Mix_Paused(channel_) != 0;
|
||||
}
|
||||
|
||||
void Sound::setVolume(float volume) {
|
||||
volume_ = volume;
|
||||
if (channel_ >= 0) {
|
||||
int mixVol = static_cast<int>(volume * MIX_MAX_VOLUME);
|
||||
Mix_Volume(channel_, mixVol);
|
||||
}
|
||||
}
|
||||
|
||||
void Sound::setLooping(bool looping) {
|
||||
looping_ = looping;
|
||||
// SDL_mixer 的循环在播放时设置,运行中无法更改
|
||||
// 如果需要即时生效,需要重新播放
|
||||
}
|
||||
|
||||
void Sound::setPitch(float pitch) {
|
||||
pitch_ = pitch;
|
||||
// SDL2_mixer 不直接支持变速播放
|
||||
// 需要更高级的音频处理才能实现
|
||||
}
|
||||
|
||||
float Sound::getDuration() const {
|
||||
// SDL2_mixer 没有直接获取时长的 API
|
||||
// 返回 0 表示不支持
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
float Sound::getCursor() const {
|
||||
// SDL2_mixer 没有直接获取播放位置的 API
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
void Sound::setCursor(float /*seconds*/) {
|
||||
// SDL2_mixer 不支持 seek 到特定位置 (对 chunks)
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#include <event/event.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Event Event::createWindowResize(int width, int height) {
|
||||
Event event;
|
||||
event.type = EventType::WindowResize;
|
||||
event.data = WindowResizeEvent{width, height};
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createWindowClose() {
|
||||
Event event;
|
||||
event.type = EventType::WindowClose;
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createKeyPress(int keyCode, int scancode, int mods) {
|
||||
Event event;
|
||||
event.type = EventType::KeyPressed;
|
||||
event.data = KeyEvent{keyCode, scancode, mods};
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createKeyRelease(int keyCode, int scancode, int mods) {
|
||||
Event event;
|
||||
event.type = EventType::KeyReleased;
|
||||
event.data = KeyEvent{keyCode, scancode, mods};
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createMouseButtonPress(int button, int mods, const Vec2 &pos) {
|
||||
Event event;
|
||||
event.type = EventType::MouseButtonPressed;
|
||||
event.data = MouseButtonEvent{button, mods, pos};
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createMouseButtonRelease(int button, int mods, const Vec2 &pos) {
|
||||
Event event;
|
||||
event.type = EventType::MouseButtonReleased;
|
||||
event.data = MouseButtonEvent{button, mods, pos};
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createMouseMove(const Vec2 &pos, const Vec2 &delta) {
|
||||
Event event;
|
||||
event.type = EventType::MouseMoved;
|
||||
event.data = MouseMoveEvent{pos, delta};
|
||||
return event;
|
||||
}
|
||||
|
||||
Event Event::createMouseScroll(const Vec2 &offset, const Vec2 &pos) {
|
||||
Event event;
|
||||
event.type = EventType::MouseScrolled;
|
||||
event.data = MouseScrollEvent{offset, pos};
|
||||
return event;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
#include <algorithm>
|
||||
#include <event/event_dispatcher.h>
|
||||
#include <event/event_queue.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
EventDispatcher::EventDispatcher() : nextId_(1) {}
|
||||
|
||||
ListenerId EventDispatcher::addListener(EventType type,
|
||||
EventCallback callback) {
|
||||
ListenerId id = nextId_++;
|
||||
listeners_[type].push_back({id, type, callback});
|
||||
return id;
|
||||
}
|
||||
|
||||
void EventDispatcher::removeListener(ListenerId id) {
|
||||
for (auto &[type, listeners] : listeners_) {
|
||||
auto it = std::remove_if(listeners.begin(), listeners.end(),
|
||||
[id](const Listener &l) { return l.id == id; });
|
||||
if (it != listeners.end()) {
|
||||
listeners.erase(it, listeners.end());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::removeAllListeners(EventType type) {
|
||||
listeners_.erase(type);
|
||||
}
|
||||
|
||||
void EventDispatcher::removeAllListeners() { listeners_.clear(); }
|
||||
|
||||
void EventDispatcher::dispatch(Event &event) {
|
||||
auto it = listeners_.find(event.type);
|
||||
if (it != listeners_.end()) {
|
||||
for (auto &listener : it->second) {
|
||||
if (event.handled)
|
||||
break;
|
||||
listener.callback(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EventDispatcher::dispatch(const Event &event) {
|
||||
Event mutableEvent = event;
|
||||
dispatch(mutableEvent);
|
||||
}
|
||||
|
||||
void EventDispatcher::processQueue(EventQueue &queue) {
|
||||
Event event;
|
||||
while (queue.poll(event)) {
|
||||
dispatch(event);
|
||||
}
|
||||
}
|
||||
|
||||
size_t EventDispatcher::getListenerCount(EventType type) const {
|
||||
auto it = listeners_.find(type);
|
||||
return (it != listeners_.end()) ? it->second.size() : 0;
|
||||
}
|
||||
|
||||
size_t EventDispatcher::getTotalListenerCount() const {
|
||||
size_t count = 0;
|
||||
for (const auto &[type, listeners] : listeners_) {
|
||||
count += listeners.size();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
#include <event/event_queue.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
EventQueue::EventQueue() = default;
|
||||
|
||||
void EventQueue::push(const Event &event) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
queue_.push(event);
|
||||
}
|
||||
|
||||
void EventQueue::push(Event &&event) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
queue_.push(std::move(event));
|
||||
}
|
||||
|
||||
bool EventQueue::poll(Event &event) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (queue_.empty()) {
|
||||
return false;
|
||||
}
|
||||
event = queue_.front();
|
||||
queue_.pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EventQueue::peek(Event &event) const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (queue_.empty()) {
|
||||
return false;
|
||||
}
|
||||
event = queue_.front();
|
||||
return true;
|
||||
}
|
||||
|
||||
void EventQueue::clear() {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
while (!queue_.empty()) {
|
||||
queue_.pop();
|
||||
}
|
||||
}
|
||||
|
||||
bool EventQueue::empty() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return queue_.empty();
|
||||
}
|
||||
|
||||
size_t EventQueue::size() const {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return queue_.size();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
#include <event/event_queue.h>
|
||||
#include <platform/input.h>
|
||||
#include <platform/window.h>
|
||||
#include <utils/logger.h>
|
||||
|
|
@ -13,8 +12,7 @@ Window::Window()
|
|||
: sdlWindow_(nullptr), glContext_(nullptr), currentCursor_(nullptr),
|
||||
width_(1280), height_(720), vsync_(true), shouldClose_(false),
|
||||
fullscreen_(true), focused_(true), contentScaleX_(1.0f),
|
||||
contentScaleY_(1.0f), enableDpiScale_(true), userData_(nullptr),
|
||||
eventQueue_(nullptr) {
|
||||
contentScaleY_(1.0f), enableDpiScale_(true), userData_(nullptr) {
|
||||
// 初始化光标数组
|
||||
for (int i = 0; i < 9; ++i) {
|
||||
sdlCursors_[i] = nullptr;
|
||||
|
|
|
|||
|
|
@ -1,444 +0,0 @@
|
|||
#include <simpleini/SimpleIni.h>
|
||||
#include <utils/data.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
|
||||
// Switch 平台特定头文件
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#include <switch/services/fs.h>
|
||||
#endif
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class DataStore::Impl {
|
||||
public:
|
||||
CSimpleIniA ini;
|
||||
};
|
||||
|
||||
DataStore::DataStore() : impl_(unique<Impl>()) {}
|
||||
|
||||
DataStore::~DataStore() {
|
||||
// 如果在事务中,尝试提交
|
||||
if (inTransaction_ && dirty_) {
|
||||
commit();
|
||||
}
|
||||
// 如果存档已挂载,卸载
|
||||
if (saveDataMounted_) {
|
||||
unmountSaveData(mountName_);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 文件操作
|
||||
// ============================================================================
|
||||
|
||||
bool DataStore::load(const std::string &filename) {
|
||||
filename_ = filename;
|
||||
SI_Error rc = impl_->ini.LoadFile(filename.c_str());
|
||||
dirty_ = false;
|
||||
return rc >= 0;
|
||||
}
|
||||
|
||||
bool DataStore::save(const std::string &filename) {
|
||||
// 如果在事务中,只标记为脏,不实际写入
|
||||
if (inTransaction_) {
|
||||
dirty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::string &targetFile = filename.empty() ? filename_ : filename;
|
||||
if (targetFile.empty()) {
|
||||
E2D_LOG_ERROR("DataStore::save: 没有指定文件名");
|
||||
return false;
|
||||
}
|
||||
|
||||
return internalSave(targetFile);
|
||||
}
|
||||
|
||||
bool DataStore::internalSave(const std::string &filename) {
|
||||
SI_Error rc = impl_->ini.SaveFile(filename.c_str());
|
||||
if (rc < 0) {
|
||||
E2D_LOG_ERROR("DataStore::save: 保存文件失败: {}", filename);
|
||||
return false;
|
||||
}
|
||||
dirty_ = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Switch 存档系统支持
|
||||
// ============================================================================
|
||||
|
||||
#ifdef __SWITCH__
|
||||
|
||||
bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
|
||||
const std::string &mountName) {
|
||||
// 如果已经挂载,先卸载
|
||||
if (saveDataMounted_) {
|
||||
unmountSaveData(mountName_);
|
||||
}
|
||||
|
||||
Result rc = 0;
|
||||
AccountUid uid = {userId.uid[0], userId.uid[1]};
|
||||
|
||||
// 如果没有提供用户ID,尝试获取当前用户
|
||||
if (type == SaveDataType::Account && !userId.isValid()) {
|
||||
UserId currentUid = getCurrentUserId();
|
||||
uid.uid[0] = currentUid.uid[0];
|
||||
uid.uid[1] = currentUid.uid[1];
|
||||
if (uid.uid[0] == 0 && uid.uid[1] == 0) {
|
||||
E2D_LOG_ERROR("DataStore::mountSaveData: 无法获取当前用户ID");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用 fsdevMountSaveData 挂载
|
||||
// 注意:这里使用当前应用程序ID (0 表示当前应用)
|
||||
u64 applicationId = 0;
|
||||
rc = fsdevMountSaveData(mountName.c_str(), applicationId, uid);
|
||||
|
||||
if (R_FAILED(rc)) {
|
||||
E2D_LOG_ERROR("DataStore::mountSaveData: 挂载失败: 0x{:X}", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
mountName_ = mountName;
|
||||
saveDataMounted_ = true;
|
||||
defaultUserId_ = UserId{uid.uid[0], uid.uid[1]};
|
||||
|
||||
E2D_LOG_INFO("DataStore::mountSaveData: 成功挂载存档: {}", mountName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DataStore::unmountSaveData(const std::string &mountName) {
|
||||
if (!saveDataMounted_) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 先提交更改
|
||||
if (dirty_) {
|
||||
commitSaveData(mountName_);
|
||||
}
|
||||
|
||||
fsdevUnmountDevice(mountName.c_str());
|
||||
saveDataMounted_ = false;
|
||||
mountName_.clear();
|
||||
|
||||
E2D_LOG_INFO("DataStore::unmountSaveData: 已卸载存档");
|
||||
}
|
||||
|
||||
bool DataStore::commitSaveData(const std::string &mountName) {
|
||||
if (!saveDataMounted_) {
|
||||
E2D_LOG_WARN("DataStore::commitSaveData: 存档未挂载");
|
||||
return false;
|
||||
}
|
||||
|
||||
Result rc = fsdevCommitDevice(mountName.c_str());
|
||||
if (R_FAILED(rc)) {
|
||||
E2D_LOG_ERROR("DataStore::commitSaveData: 提交失败: 0x{:X}", rc);
|
||||
return false;
|
||||
}
|
||||
|
||||
E2D_LOG_DEBUG("DataStore::commitSaveData: 提交成功");
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DataStore::getSaveDataPath(const std::string &path) const {
|
||||
if (!saveDataMounted_) {
|
||||
return path;
|
||||
}
|
||||
return mountName_ + ":/" + path;
|
||||
}
|
||||
|
||||
UserId DataStore::getCurrentUserId() {
|
||||
UserId result;
|
||||
|
||||
Result rc = accountInitialize(AccountServiceType_Application);
|
||||
if (R_FAILED(rc)) {
|
||||
E2D_LOG_ERROR("DataStore::getCurrentUserId: accountInitialize 失败: 0x{:X}",
|
||||
rc);
|
||||
return result;
|
||||
}
|
||||
|
||||
AccountUid uid;
|
||||
rc = accountGetPreselectedUser(&uid);
|
||||
accountExit();
|
||||
|
||||
if (R_SUCCEEDED(rc)) {
|
||||
result.uid[0] = uid.uid[0];
|
||||
result.uid[1] = uid.uid[1];
|
||||
E2D_LOG_DEBUG("DataStore::getCurrentUserId: 获取成功: 0x{:X}{:X}",
|
||||
result.uid[1], result.uid[0]);
|
||||
} else {
|
||||
E2D_LOG_ERROR("DataStore::getCurrentUserId: 获取失败: 0x{:X}", rc);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// 非 Switch 平台的存根实现
|
||||
|
||||
bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
|
||||
const std::string &mountName) {
|
||||
(void)type;
|
||||
(void)userId;
|
||||
(void)mountName;
|
||||
E2D_LOG_WARN("DataStore::mountSaveData: 非 Switch 平台,存档功能不可用");
|
||||
return false;
|
||||
}
|
||||
|
||||
void DataStore::unmountSaveData(const std::string &mountName) {
|
||||
(void)mountName;
|
||||
saveDataMounted_ = false;
|
||||
}
|
||||
|
||||
bool DataStore::commitSaveData(const std::string &mountName) {
|
||||
(void)mountName;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string DataStore::getSaveDataPath(const std::string &path) const {
|
||||
return path;
|
||||
}
|
||||
|
||||
UserId DataStore::getCurrentUserId() { return UserId(); }
|
||||
|
||||
#endif
|
||||
|
||||
// ============================================================================
|
||||
// 数据读写
|
||||
// ============================================================================
|
||||
|
||||
std::string DataStore::getString(const std::string §ion,
|
||||
const std::string &key,
|
||||
const std::string &defaultValue) {
|
||||
const char *value =
|
||||
impl_->ini.GetValue(section.c_str(), key.c_str(), defaultValue.c_str());
|
||||
return value ? value : defaultValue;
|
||||
}
|
||||
|
||||
int DataStore::getInt(const std::string §ion, const std::string &key,
|
||||
int defaultValue) {
|
||||
return static_cast<int>(
|
||||
impl_->ini.GetLongValue(section.c_str(), key.c_str(), defaultValue));
|
||||
}
|
||||
|
||||
float DataStore::getFloat(const std::string §ion, const std::string &key,
|
||||
float defaultValue) {
|
||||
const char *value =
|
||||
impl_->ini.GetValue(section.c_str(), key.c_str(), nullptr);
|
||||
if (value) {
|
||||
try {
|
||||
return std::stof(value);
|
||||
} catch (...) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
bool DataStore::getBool(const std::string §ion, const std::string &key,
|
||||
bool defaultValue) {
|
||||
return impl_->ini.GetBoolValue(section.c_str(), key.c_str(), defaultValue);
|
||||
}
|
||||
|
||||
void DataStore::setString(const std::string §ion, const std::string &key,
|
||||
const std::string &value) {
|
||||
impl_->ini.SetValue(section.c_str(), key.c_str(), value.c_str());
|
||||
dirty_ = true;
|
||||
|
||||
// 不在事务中时自动保存
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
void DataStore::setInt(const std::string §ion, const std::string &key,
|
||||
int value) {
|
||||
impl_->ini.SetLongValue(section.c_str(), key.c_str(), value);
|
||||
dirty_ = true;
|
||||
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
void DataStore::setFloat(const std::string §ion, const std::string &key,
|
||||
float value) {
|
||||
impl_->ini.SetValue(section.c_str(), key.c_str(),
|
||||
std::to_string(value).c_str());
|
||||
dirty_ = true;
|
||||
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
void DataStore::setBool(const std::string §ion, const std::string &key,
|
||||
bool value) {
|
||||
impl_->ini.SetBoolValue(section.c_str(), key.c_str(), value);
|
||||
dirty_ = true;
|
||||
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
void DataStore::removeKey(const std::string §ion, const std::string &key) {
|
||||
impl_->ini.Delete(section.c_str(), key.c_str());
|
||||
dirty_ = true;
|
||||
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
void DataStore::removeSection(const std::string §ion) {
|
||||
impl_->ini.Delete(section.c_str(), nullptr);
|
||||
dirty_ = true;
|
||||
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
bool DataStore::hasKey(const std::string §ion, const std::string &key) {
|
||||
return impl_->ini.GetValue(section.c_str(), key.c_str(), nullptr) != nullptr;
|
||||
}
|
||||
|
||||
bool DataStore::hasSection(const std::string §ion) {
|
||||
return impl_->ini.GetSection(section.c_str()) != nullptr;
|
||||
}
|
||||
|
||||
void DataStore::clear() {
|
||||
impl_->ini.Reset();
|
||||
dirty_ = true;
|
||||
|
||||
if (!inTransaction_ && !filename_.empty()) {
|
||||
save("");
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 事务支持
|
||||
// ============================================================================
|
||||
|
||||
void DataStore::beginTransaction() {
|
||||
if (inTransaction_) {
|
||||
E2D_LOG_WARN("DataStore::beginTransaction: 已经处于事务中");
|
||||
return;
|
||||
}
|
||||
|
||||
inTransaction_ = true;
|
||||
dirty_ = false;
|
||||
|
||||
E2D_LOG_DEBUG("DataStore::beginTransaction: 事务开始");
|
||||
}
|
||||
|
||||
bool DataStore::commit() {
|
||||
if (!inTransaction_) {
|
||||
E2D_LOG_WARN("DataStore::commit: 不在事务中");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果有文件名,写入文件
|
||||
bool result = true;
|
||||
if (!filename_.empty() && dirty_) {
|
||||
result = internalSave(filename_);
|
||||
|
||||
// 如果挂载了存档,提交更改
|
||||
if (result && saveDataMounted_) {
|
||||
result = commitSaveData(mountName_);
|
||||
}
|
||||
}
|
||||
|
||||
inTransaction_ = false;
|
||||
|
||||
E2D_LOG_DEBUG("DataStore::commit: 事务提交 {}", result ? "成功" : "失败");
|
||||
return result;
|
||||
}
|
||||
|
||||
void DataStore::rollback() {
|
||||
if (!inTransaction_) {
|
||||
E2D_LOG_WARN("DataStore::rollback: 不在事务中");
|
||||
return;
|
||||
}
|
||||
|
||||
// 重新加载文件来恢复数据
|
||||
if (!filename_.empty()) {
|
||||
impl_->ini.Reset();
|
||||
SI_Error rc = impl_->ini.LoadFile(filename_.c_str());
|
||||
if (rc < 0) {
|
||||
E2D_LOG_ERROR("DataStore::rollback: 重新加载文件失败: {}", filename_);
|
||||
}
|
||||
} else {
|
||||
// 如果没有文件名,清空数据
|
||||
impl_->ini.Reset();
|
||||
}
|
||||
|
||||
inTransaction_ = false;
|
||||
dirty_ = false;
|
||||
|
||||
E2D_LOG_DEBUG("DataStore::rollback: 事务已回滚");
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// 工具方法
|
||||
// ============================================================================
|
||||
|
||||
std::vector<std::string> DataStore::getAllSections() const {
|
||||
std::vector<std::string> sections;
|
||||
CSimpleIniA::TNamesDepend sectionList;
|
||||
impl_->ini.GetAllSections(sectionList);
|
||||
|
||||
for (const auto §ion : sectionList) {
|
||||
sections.emplace_back(section.pItem);
|
||||
}
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
DataStore::getAllKeys(const std::string §ion) const {
|
||||
std::vector<std::string> keys;
|
||||
CSimpleIniA::TNamesDepend keyList;
|
||||
impl_->ini.GetAllKeys(section.c_str(), keyList);
|
||||
|
||||
for (const auto &key : keyList) {
|
||||
keys.emplace_back(key.pItem);
|
||||
}
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
bool DataStore::loadFromSave(const std::string &path) {
|
||||
if (!saveDataMounted_) {
|
||||
E2D_LOG_ERROR("DataStore::loadFromSave: 存档未挂载");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string fullPath = getSaveDataPath(path);
|
||||
return load(fullPath);
|
||||
}
|
||||
|
||||
bool DataStore::saveToSave(const std::string &path) {
|
||||
if (!saveDataMounted_) {
|
||||
E2D_LOG_ERROR("DataStore::saveToSave: 存档未挂载");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string fullPath = getSaveDataPath(path);
|
||||
bool result = save(fullPath);
|
||||
|
||||
// 自动提交
|
||||
if (result) {
|
||||
result = commitSaveData(mountName_);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue