diff --git a/include/app/application.h b/include/app/application.h index 3b202d2..eab9cf1 100644 --- a/include/app/application.h +++ b/include/app/application.h @@ -2,9 +2,13 @@ #include #include +#include namespace extra2d { +// 前向声明 +class Context; + /** * @brief 应用程序配置 */ @@ -24,34 +28,40 @@ struct AppConfig { /** * @brief 应用程序主控类 - * + * * 管理应用程序生命周期、窗口和主循环 + * 基于新的 Context 架构,支持多实例 */ class Application { public: /** - * @brief 获取单例实例 + * @brief 创建应用程序实例 */ - static Application& instance(); + static std::unique_ptr create(); Application(const Application&) = delete; Application& operator=(const Application&) = delete; + Application(Application&&) noexcept; + Application& operator=(Application&&) noexcept; + + ~Application(); + /** * @brief 初始化应用程序 */ bool init(const AppConfig& config); - + /** * @brief 关闭应用程序 */ void shutdown(); - + /** * @brief 运行主循环 */ void run(); - + /** * @brief 请求退出 */ @@ -61,17 +71,17 @@ public: * @brief 暂停应用 */ void pause(); - + /** * @brief 恢复应用 */ void resume(); - + /** * @brief 是否暂停 */ bool isPaused() const { return paused_; } - + /** * @brief 是否运行中 */ @@ -81,12 +91,12 @@ public: * @brief 获取帧时间 */ float deltaTime() const { return deltaTime_; } - + /** * @brief 获取总运行时间 */ float totalTime() const { return totalTime_; } - + /** * @brief 获取当前 FPS */ @@ -97,13 +107,19 @@ public: */ const AppConfig& getConfig() const { return config_; } + /** + * @brief 获取引擎上下文 + */ + Context* getContext() const { return context_.get(); } + private: - Application() = default; - ~Application(); + Application(); void mainLoop(); void update(); + std::unique_ptr context_; + AppConfig config_; bool initialized_ = false; @@ -119,6 +135,4 @@ private: int32 currentFps_ = 0; }; -#define APP extra2d::Application::instance() - } // namespace extra2d diff --git a/include/context/context.h b/include/context/context.h new file mode 100644 index 0000000..4b320b9 --- /dev/null +++ b/include/context/context.h @@ -0,0 +1,100 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// 前向声明 +class ModuleRegistry; +class PluginLoader; +class TimerModule; + +/** + * @brief 引擎上下文 + * + * 管理引擎的核心生命周期,包含模块注册表、插件加载器和定时器模块 + * 非单例设计,支持创建多个独立的引擎实例 + */ +class Context { +public: + Context(); + ~Context(); + + // 禁止拷贝 + Context(const Context&) = delete; + Context& operator=(const Context&) = delete; + + // 允许移动 + Context(Context&&) noexcept; + Context& operator=(Context&&) noexcept; + + /** + * @brief 创建上下文(工厂方法) + */ + static std::unique_ptr create(); + + /** + * @brief 初始化引擎 + * @return 初始化是否成功 + */ + bool init(); + + /** + * @brief 关闭引擎 + */ + void shutdown(); + + /** + * @brief 主循环单帧更新 + * @param dt 帧间隔时间(秒) + */ + void tick(float dt); + + /** + * @brief 获取模块注册表 + */ + ModuleRegistry& modules(); + + /** + * @brief 获取插件加载器 + */ + PluginLoader& plugins(); + + /** + * @brief 获取定时器模块 + */ + TimerModule& timer(); + + /** + * @brief 获取总运行时间 + */ + float totalTime() const { return totalTime_; } + + /** + * @brief 获取帧数 + */ + uint64 frameCount() const { return frameCount_; } + + /** + * @brief 检查是否正在运行 + */ + bool isRunning() const { return running_; } + + /** + * @brief 请求停止引擎 + */ + void stop() { running_ = false; } + +private: + std::unique_ptr moduleRegistry_; + std::unique_ptr pluginLoader_; + std::unique_ptr timerModule_; + + float totalTime_ = 0.0f; + uint64 frameCount_ = 0; + bool running_ = false; + bool inited_ = false; +}; + +} // namespace extra2d diff --git a/include/core/director.h b/include/core/director.h deleted file mode 100644 index 5ebe532..0000000 --- a/include/core/director.h +++ /dev/null @@ -1,50 +0,0 @@ -#pragma once - -#include -#include - -namespace extra2d { - -/** - * @brief 导演类 - * - * 主循环管理器,协调调度器和服务管理器 - */ -class Director { -public: - static Director& inst(); - - bool init(); - void shutdown(); - - void mainLoop(float dt); - void mainLoopParallel(float dt); - - Scheduler& sched() { return SCHED; } - SvcMgr& svcs() { return SVC_MGR; } - - float dt() const { return dt_; } - float totalTime() const { return totalTime_; } - uint64 frameCount() const { return frameCount_; } - - void pause(); - void resume(); - bool isPaused() const { return paused_; } - - void setTimeScale(float scale); - -private: - Director() = default; - - float dt_ = 0.0f; - float totalTime_ = 0.0f; - float fixedAccumulator_ = 0.0f; - float fixedDt_ = 1.0f / 60.0f; - uint64 frameCount_ = 0; - bool paused_ = false; - bool inited_ = false; -}; - -#define DIRECTOR extra2d::Director::inst() - -} // namespace extra2d diff --git a/include/core/scheduler.h b/include/core/scheduler.h deleted file mode 100644 index 3502e73..0000000 --- a/include/core/scheduler.h +++ /dev/null @@ -1,140 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -using TimerHdl = uint32; -constexpr TimerHdl INVALID_HDL = 0; - -/** - * @brief 定时器目标接口 - * - * 实现此接口的对象可以接收 update 回调 - */ -class TimerTarget { -public: - virtual ~TimerTarget() = default; - virtual void update(float dt) = 0; -}; - -/** - * @brief 定时器基类 - */ -class Timer : public RefCounted { -public: - virtual ~Timer() = default; - - virtual void update(float dt) = 0; - virtual void trigger() = 0; - - bool isPaused() const { return paused_; } - bool isDone() const { return done_; } - TimerHdl hdl() const { return hdl_; } - - void pause() { paused_ = true; } - void resume() { paused_ = false; } - void cancel() { done_ = true; } - -protected: - Timer() = default; - - float elapsed_ = -1.0f; - float interval_ = 0.0f; - float delay_ = 0.0f; - uint32 repeat_ = 0; - uint32 timesExecuted_ = 0; - bool useDelay_ = false; - bool runForever_ = false; - bool paused_ = false; - bool done_ = false; - TimerHdl hdl_ = INVALID_HDL; - - friend class Scheduler; -}; - -/** - * @brief 调度器 - * - * 基于标准库实现的线程安全调度器,支持定时器和 update 回调 - */ -class Scheduler { -public: - using Cb = Fn; - using VoidCb = Fn; - - static Scheduler& inst(); - - TimerHdl scheduleUpdate(TimerTarget* target, int pri = 0); - void unscheduleUpdate(TimerTarget* target); - - TimerHdl schedule(Cb cb, float interval, uint32 repeat = 0, float delay = 0.0f); - TimerHdl scheduleOnce(VoidCb cb, float delay); - TimerHdl scheduleForever(Cb cb, float interval); - - void unschedule(TimerHdl hdl); - void unscheduleAll(); - void pause(TimerHdl hdl); - void resume(TimerHdl hdl); - - void setTimeScale(float scale) { timeScale_ = scale; } - float timeScale() const { return timeScale_; } - - void update(float dt); - void updateParallel(float dt); - - bool isScheduled(TimerHdl hdl) const; - size_t count() const; - -private: - Scheduler() = default; - - struct UpdateEntry { - TimerTarget* target; - int pri; - bool paused; - bool markedForDel; - - bool operator<(const UpdateEntry& o) const { return pri > o.pri; } - }; - - // 线程安全的优先队列包装 - class SafePriorityQueue { - public: - void push(const UpdateEntry& entry); - bool pop(UpdateEntry& entry); - bool empty() const; - size_t size() const; - void clear(); - private: - mutable std::mutex mutex_; - std::priority_queue queue_; - }; - - std::vector updates_; - std::unordered_map updateIndex_; - mutable std::mutex updateIndexMutex_; - - std::unordered_map> timers_; - mutable std::mutex timersMutex_; - - SafePriorityQueue updateQueue_; - - std::atomic nextHdl_{1}; - std::atomic timeScale_{1.0f}; - std::atomic locked_{false}; - - TimerHdl genHdl(); -}; - -#define SCHED extra2d::Scheduler::inst() - -} // namespace extra2d diff --git a/include/core/service.h b/include/core/service.h deleted file mode 100644 index 91cc8dd..0000000 --- a/include/core/service.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace extra2d { - -/** - * @brief 服务状态枚举 - */ -enum class SvcState : uint8 { - None, - Inited, - Running, - Paused, - Shutdown -}; - -/** - * @brief 服务基类 - * - * 所有子系统服务都继承此类,提供统一的生命周期管理 - */ -class IService : public RefCounted { -public: - virtual ~IService() = default; - - virtual bool init() { return true; } - virtual void shutdown() {} - virtual void pause() {} - virtual void resume() {} - - virtual void update(float dt) {} - virtual void lateUpdate(float dt) {} - virtual void fixedUpdate(float dt) {} - - virtual const char* name() const = 0; - virtual int pri() const { return Pri::Default; } - - SvcState state() const { return state_; } - bool isInited() const { return state_ >= SvcState::Inited; } - bool isEnabled() const { return enabled_; } - void setEnabled(bool v) { enabled_ = v; } - -protected: - IService() = default; - SvcState state_ = SvcState::None; - bool enabled_ = true; - - friend class SvcMgr; -}; - -/** - * @brief 服务管理器 - * - * 管理所有服务的注册、初始化、更新和关闭 - */ -class SvcMgr { -public: - static SvcMgr& inst(); - - void reg(IService* svc); - void unreg(const char* name); - Ptr get(const char* name); - - template - Ptr getAs(const char* name) { - return static_cast(get(name).get()); - } - - bool initAll(); - void pauseAll(); - void resumeAll(); - void shutdownAll(); - - void updateAll(float dt); - void lateUpdateAll(float dt); - void fixedUpdateAll(float dt); - - bool has(const char* name) const; - size_t count() const; - -private: - SvcMgr() = default; - - using SvcMap = std::unordered_map>; - SvcMap svcMap_; - mutable std::mutex svcMapMutex_; - - std::vector> sortedSvcs_; - - void sortSvcs(); -}; - -#define SVC_MGR extra2d::SvcMgr::inst() - -} // namespace extra2d diff --git a/include/event/events.h b/include/event/events.h index 22a2a80..3963ce9 100644 --- a/include/event/events.h +++ b/include/event/events.h @@ -137,27 +137,47 @@ DECLARE_EVENT_1(OnMouseWheel, Engine, int32) /** * @brief 触摸开始事件 - * @param touch 触摸点信息 + * @param touchId 触摸点ID + * @param x X 坐标 + * @param y Y 坐标 */ -DECLARE_EVENT_1(OnTouchBegan, Engine, const TouchPoint &) +DECLARE_EVENT_3(OnTouchBegin, Engine, int32, int32, int32) /** * @brief 触摸移动事件 - * @param touch 触摸点信息 + * @param touchId 触摸点ID + * @param x X 坐标 + * @param y Y 坐标 */ -DECLARE_EVENT_1(OnTouchMoved, Engine, const TouchPoint &) +DECLARE_EVENT_3(OnTouchMove, Engine, int32, int32, int32) /** * @brief 触摸结束事件 - * @param touch 触摸点信息 + * @param touchId 触摸点ID + * @param x X 坐标 + * @param y Y 坐标 */ -DECLARE_EVENT_1(OnTouchEnded, Engine, const TouchPoint &) +DECLARE_EVENT_3(OnTouchEnd, Engine, int32, int32, int32) /** * @brief 触摸取消事件 - * @param touch 触摸点信息 + * @param touchId 触摸点ID + * @param x X 坐标 + * @param y Y 坐标 */ -DECLARE_EVENT_1(OnTouchCancelled, Engine, const TouchPoint &) +DECLARE_EVENT_3(OnTouchCancel, Engine, int32, int32, int32) + +/** + * @brief 游戏手柄连接事件 + * @param deviceId 设备ID + */ +DECLARE_EVENT_1(OnGamepadConnect, Engine, int32) + +/** + * @brief 游戏手柄断开事件 + * @param deviceId 设备ID + */ +DECLARE_EVENT_1(OnGamepadDisconnect, Engine, int32) // ============================================================================ // 场景事件 diff --git a/include/extra2d.h b/include/extra2d.h index b3de0b3..70deb38 100644 --- a/include/extra2d.h +++ b/include/extra2d.h @@ -17,21 +17,24 @@ #include #include -// Core -#include -#include -#include +// Context (核心上下文) +#include + +// Module System (新架构 - 基于事件总线) +#include +#include + +// Plugin System (新架构 - 基于事件总线) +#include +#include + +// Event System +#include +#include +#include // Platform -#include -#include #include -#include - - -// Event -#include -#include // Utils #include @@ -40,6 +43,13 @@ // Application #include +// 兼容层 - 旧的头文件(将在后续版本中移除) +// #include // 已废弃,使用 Context +// #include // 已废弃,使用 imodule.h +// #include // 已废弃,使用 module_registry.h +// #include // 已废弃,使用 iplugin.h +// #include // 已废弃,使用 plugin_loader.h + #ifdef __SWITCH__ #include #endif diff --git a/include/module/imodule.h b/include/module/imodule.h new file mode 100644 index 0000000..08214f6 --- /dev/null +++ b/include/module/imodule.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +namespace extra2d { + +/** + * @brief 模块类型枚举 + */ +enum class ModuleType : uint8 { + Core, // 核心模块(必须) + System, // 系统模块(平台相关) + Feature, // 功能模块 + Extension // 扩展模块 +}; + +/** + * @brief 模块接口 - 基于事件总线 + * + * 模块不再通过虚函数被调用,而是通过监听事件响应 + * 模块可以发送事件,但不应直接调用其他模块 + * + * 设计理念: + * - 模块是引擎的内置组件,在编译时确定 + * - 模块之间通过事件总线通信,零直接依赖 + * - 模块的生命周期由 ModuleRegistry 管理 + */ +class IModule { +public: + virtual ~IModule() = default; + + /** + * @brief 获取模块名称 + * @return 模块唯一标识名 + */ + virtual const char* name() const = 0; + + /** + * @brief 获取模块类型 + * @return 模块类型,用于分类和优先级管理 + */ + virtual ModuleType type() const { return ModuleType::Feature; } + + /** + * @brief 获取模块优先级 + * @return 优先级数值,越小优先级越高 + */ + virtual int priority() const { return 0; } + + /** + * @brief 初始化模块 + * + * 在此注册事件监听器 + * 返回 false 表示初始化失败,引擎将停止 + * + * @return 初始化是否成功 + */ + virtual bool init() { return true; } + + /** + * @brief 关闭模块 + * + * 在此执行清理工作 + * 事件监听器会自动注销(RAII) + */ + virtual void shutdown() {} +}; + +} // namespace extra2d diff --git a/include/module/module_registry.h b/include/module/module_registry.h new file mode 100644 index 0000000..6fda2af --- /dev/null +++ b/include/module/module_registry.h @@ -0,0 +1,97 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 模块注册表 - 非单例 + * + * 管理所有模块的生命周期,通过事件总线协调模块间的通信 + * 支持按优先级排序初始化和关闭 + */ +class ModuleRegistry { +public: + ModuleRegistry(); + ~ModuleRegistry(); + + // 禁止拷贝 + ModuleRegistry(const ModuleRegistry &) = delete; + ModuleRegistry &operator=(const ModuleRegistry &) = delete; + + // 允许移动 + ModuleRegistry(ModuleRegistry &&) noexcept; + ModuleRegistry &operator=(ModuleRegistry &&) noexcept; + + /** + * @brief 注册模块 + * @param module 模块实例指针(不由注册表管理生命周期) + */ + void registerModule(IModule *module); + + /** + * @brief 注销模块 + * @param name 模块名称 + */ + void unregisterModule(const char *name); + + /** + * @brief 获取模块 + * @param name 模块名称 + * @return 模块指针,不存在返回 nullptr + */ + IModule *getModule(const char *name) const; + + /** + * @brief 检查模块是否存在 + * @param name 模块名称 + * @return 是否存在 + */ + bool hasModule(const char *name) const; + + /** + * @brief 获取所有模块 + * @return 模块列表 + */ + std::vector getAllModules() const; + + /** + * @brief 获取指定类型的所有模块 + * @param type 模块类型 + * @return 模块列表 + */ + std::vector getModulesByType(ModuleType type) const; + + /** + * @brief 初始化所有模块(按优先级排序) + * @return 是否全部初始化成功 + */ + bool initAll(); + + /** + * @brief 关闭所有模块(按优先级逆序) + */ + void shutdownAll(); + + /** + * @brief 获取模块数量 + * @return 模块数量 + */ + size_t getModuleCount() const; + +private: + /** + * @brief 按优先级排序模块 + */ + void sortModules(); + + std::vector modules_; + std::unordered_map moduleMap_; + bool sorted_ = false; + bool inited_ = false; +}; + +} // namespace extra2d diff --git a/include/module/timer_module.h b/include/module/timer_module.h new file mode 100644 index 0000000..3a5a8bd --- /dev/null +++ b/include/module/timer_module.h @@ -0,0 +1,149 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 定时器句柄 + */ +using TimerId = uint32; +constexpr TimerId INVALID_TIMER_ID = 0; + +/** + * @brief 定时器回调类型 + */ +using TimerCallback = std::function; +using TimerUpdateCallback = std::function; + +/** + * @brief 定时器信息 + */ +struct TimerInfo { + TimerId id = INVALID_TIMER_ID; + float interval = 0.0f; // 间隔时间(秒) + float elapsed = 0.0f; // 已过去的时间 + uint32 repeat = 0; // 重复次数(0表示无限) + uint32 executed = 0; // 已执行次数 + bool paused = false; // 是否暂停 + bool cancelled = false; // 是否取消 + TimerCallback callback; // 回调函数 + TimerUpdateCallback updateCallback; // 带dt的回调 +}; + +/** + * @brief 定时器模块 + * + * 提供定时任务管理功能,作为引擎核心模块运行 + * 通过事件总线接收更新事件,无需直接依赖 + */ +class TimerModule : public IModule { +public: + TimerModule(); + ~TimerModule() override; + + // 禁止拷贝 + TimerModule(const TimerModule&) = delete; + TimerModule& operator=(const TimerModule&) = delete; + + // 允许移动 + TimerModule(TimerModule&&) noexcept; + TimerModule& operator=(TimerModule&&) noexcept; + + // IModule 接口实现 + const char* name() const override { return "Timer"; } + ModuleType type() const override { return ModuleType::Core; } + int priority() const override { return Pri::System; } + bool init() override; + void shutdown() override; + + /** + * @brief 调度一次性任务 + * @param delay 延迟时间(秒) + * @param callback 回调函数 + * @return 定时器ID + */ + TimerId scheduleOnce(float delay, TimerCallback callback); + + /** + * @brief 调度重复任务 + * @param interval 间隔时间(秒) + * @param repeat 重复次数(0表示无限) + * @param callback 回调函数 + * @return 定时器ID + */ + TimerId scheduleRepeat(float interval, uint32 repeat, TimerCallback callback); + + /** + * @brief 调度每帧更新任务 + * @param callback 回调函数(接收dt) + * @return 定时器ID + */ + TimerId scheduleUpdate(TimerUpdateCallback callback); + + /** + * @brief 取消定时器 + * @param id 定时器ID + */ + void cancel(TimerId id); + + /** + * @brief 暂停定时器 + * @param id 定时器ID + */ + void pause(TimerId id); + + /** + * @brief 恢复定时器 + * @param id 定时器ID + */ + void resume(TimerId id); + + /** + * @brief 设置时间缩放 + * @param scale 时间缩放比例(1.0为正常) + */ + void setTimeScale(float scale) { timeScale_ = scale; } + + /** + * @brief 获取时间缩放 + */ + float getTimeScale() const { return timeScale_; } + + /** + * @brief 取消所有定时器 + */ + void cancelAll(); + + /** + * @brief 获取活动定时器数量 + */ + size_t getActiveCount() const; + + /** + * @brief 更新所有定时器(由 Context 调用) + * @param dt 帧间隔时间 + */ + void update(float dt); + +private: + /** + * @brief 生成唯一ID + */ + TimerId generateId(); + + std::unordered_map> timers_; + std::vector pendingRemove_; + TimerId nextId_ = 1; + float timeScale_ = 1.0f; + bool inUpdate_ = false; +}; + +} // namespace extra2d diff --git a/include/platform/file.h b/include/platform/file.h index dd4cc15..a9e9935 100644 --- a/include/platform/file.h +++ b/include/platform/file.h @@ -1,10 +1,10 @@ #pragma once +#include #include -#include +#include #include #include -#include namespace extra2d { @@ -25,132 +25,136 @@ struct FileData { bool ok = false; std::vector data; std::string error; - + operator bool() const { return ok; } const uint8* ptr() const { return data.data(); } size_t size() const { return data.size(); } }; /** - * @brief 文件服务 - * + * @brief 文件模块 + * * 提供跨平台文件系统操作 + * 非单例设计,通过 Context 管理生命周期 */ -class FileSvc : public IService { +class FileModule : public IModule { public: - /** - * @brief 获取单例实例 - */ - static FileSvc& inst(); - - const char* name() const override { return "FileSvc"; } - int pri() const override { return Pri::System; } - + FileModule(); + ~FileModule() override; + + // 禁止拷贝 + FileModule(const FileModule&) = delete; + FileModule& operator=(const FileModule&) = delete; + + // 允许移动 + FileModule(FileModule&&) noexcept; + FileModule& operator=(FileModule&&) noexcept; + + // IModule 接口实现 + const char* name() const override { return "File"; } + ModuleType type() const override { return ModuleType::System; } + int priority() const override { return Pri::File; } bool init() override; void shutdown() override; - + /** * @brief 检查文件是否存在 */ bool exists(const std::string& path) const; - + /** * @brief 检查是否为目录 */ bool isDir(const std::string& path) const; - + /** * @brief 读取整个文件到内存 */ FileData read(const std::string& path) const; - + /** * @brief 读取文件为字符串 */ std::string readString(const std::string& path) const; - + /** * @brief 写入数据到文件 */ bool write(const std::string& path, const void* data, size_t size) const; - + /** * @brief 写入字符串到文件 */ bool writeString(const std::string& path, const std::string& content) const; - + /** * @brief 追加数据到文件 */ bool append(const std::string& path, const void* data, size_t size) const; - + /** * @brief 删除文件 */ bool remove(const std::string& path) const; - + /** * @brief 创建目录 */ bool mkdir(const std::string& path) const; - + /** * @brief 列出目录内容 */ std::vector listDir(const std::string& path) const; - + /** * @brief 获取文件大小 */ int64 fileSize(const std::string& path) const; - + /** * @brief 获取文件扩展名 */ std::string ext(const std::string& path) const; - + /** * @brief 获取文件名(不含路径) */ std::string fileName(const std::string& path) const; - + /** * @brief 获取文件所在目录 */ std::string dirName(const std::string& path) const; - + /** * @brief 连接路径 */ std::string join(const std::string& a, const std::string& b) const; - + /** * @brief 获取可写目录(用于存档等) */ std::string writableDir() const; - + /** * @brief 设置资源根目录 */ void setAssetRoot(const std::string& root) { assetRoot_ = root; } - + /** * @brief 获取资源根目录 */ const std::string& assetRoot() const { return assetRoot_; } - + /** * @brief 获取资源完整路径 */ std::string assetPath(const std::string& relPath) const; - + private: - FileSvc() = default; - std::string assetRoot_; std::string writableDir_; }; -#define FILE_SVC extra2d::FileSvc::inst() - } // namespace extra2d diff --git a/include/platform/input.h b/include/platform/input.h index 880bda8..55aee26 100644 --- a/include/platform/input.h +++ b/include/platform/input.h @@ -2,9 +2,10 @@ #include #include -#include +#include +#include #include -#include +#include #include namespace extra2d { @@ -94,47 +95,47 @@ constexpr Key RightAlt = SDL_SCANCODE_RALT; * @brief 鼠标按键 */ enum class MouseBtn : uint8 { - Left = 0, - Middle = 1, - Right = 2, - X1 = 3, - X2 = 4, - Count = 5 + Left = 0, + Middle = 1, + Right = 2, + X1 = 3, + X2 = 4, + Count = 5 }; /** * @brief 游戏手柄按键 */ enum class GamepadBtn : uint8 { - A = 0, - B = 1, - X = 2, - Y = 3, - Back = 4, - Guide = 5, - Start = 6, - LeftStick = 7, - RightStick = 8, - LeftShoulder = 9, - RightShoulder = 10, - DPadUp = 11, - DPadDown = 12, - DPadLeft = 13, - DPadRight = 14, - Count = 15 + A = 0, + B = 1, + X = 2, + Y = 3, + Back = 4, + Guide = 5, + Start = 6, + LeftStick = 7, + RightStick = 8, + LeftShoulder = 9, + RightShoulder = 10, + DPadUp = 11, + DPadDown = 12, + DPadLeft = 13, + DPadRight = 14, + Count = 15 }; /** * @brief 游戏手柄轴 */ enum class GamepadAxis : uint8 { - LeftX = 0, - LeftY = 1, - RightX = 2, - RightY = 3, - TriggerLeft = 4, - TriggerRight = 5, - Count = 6 + LeftX = 0, + LeftY = 1, + RightX = 2, + RightY = 3, + TriggerLeft = 4, + TriggerRight = 5, + Count = 6 }; /** @@ -146,224 +147,232 @@ enum class TouchState : uint8 { None = 0, Began, Moved, Ended, Cancelled }; * @brief 触摸点信息 */ struct TouchPoint { - int64 id = 0; - float x = 0.0f; - float y = 0.0f; - float prevX = 0.0f; - float prevY = 0.0f; - float deltaX = 0.0f; - float deltaY = 0.0f; - TouchState state = TouchState::None; - float pressure = 1.0f; + int64 id = 0; + float x = 0.0f; + float y = 0.0f; + float prevX = 0.0f; + float prevY = 0.0f; + float deltaX = 0.0f; + float deltaY = 0.0f; + TouchState state = TouchState::None; + float pressure = 1.0f; }; /** * @brief 按键回调类型 */ -using KeyCb = Fn; -using MouseBtnCb = Fn; -using TouchCb = Fn; +using KeyCb = std::function; +using MouseBtnCb = std::function; +using TouchCb = std::function; /** - * @brief 输入服务 + * @brief 输入模块 * * 管理键盘、鼠标、触摸、游戏手柄输入 + * 非单例设计,通过 Context 管理生命周期 */ -class InputSvc : public IService { +class InputModule : public IModule { public: - /** - * @brief 获取单例实例 - */ - static InputSvc &inst(); + InputModule(); + ~InputModule() override; - const char *name() const override { return "InputSvc"; } - int pri() const override { return Pri::Input; } + // 禁止拷贝 + InputModule(const InputModule&) = delete; + InputModule& operator=(const InputModule&) = delete; - bool init() override; - void shutdown() override; - void update(float dt) override; + // 允许移动 + InputModule(InputModule&&) noexcept; + InputModule& operator=(InputModule&&) noexcept; - // ========== 键盘 ========== + // IModule 接口实现 + const char* name() const override { return "Input"; } + ModuleType type() const override { return ModuleType::System; } + int priority() const override { return Pri::Input; } + bool init() override; + void shutdown() override; - /** - * @brief 检查按键是否按下 - */ - bool isKeyDown(Key key) const; + /** + * @brief 处理输入事件 + */ + void processEvent(const SDL_Event& evt); - /** - * @brief 检查按键是否刚按下 - */ - bool isKeyPressed(Key key) const; + /** + * @brief 更新输入状态(每帧调用) + */ + void update(); - /** - * @brief 检查按键是否刚释放 - */ - bool isKeyReleased(Key key) const; + // ========== 键盘 ========== - // ========== 鼠标 ========== + /** + * @brief 检查按键是否按下 + */ + bool isKeyDown(Key key) const; - /** - * @brief 获取鼠标位置 - */ - void getMousePos(int32 &x, int32 &y) const; + /** + * @brief 检查按键是否刚按下 + */ + bool isKeyPressed(Key key) const; - /** - * @brief 获取鼠标位置(浮点) - */ - void getMousePos(float &x, float &y) const; + /** + * @brief 检查按键是否刚释放 + */ + bool isKeyReleased(Key key) const; - /** - * @brief 检查鼠标按键是否按下 - */ - bool isMouseBtnDown(MouseBtn btn) const; + // ========== 鼠标 ========== - /** - * @brief 检查鼠标按键是否刚按下 - */ - bool isMouseBtnPressed(MouseBtn btn) const; + /** + * @brief 获取鼠标位置 + */ + void getMousePos(int32& x, int32& y) const; - /** - * @brief 检查鼠标按键是否刚释放 - */ - bool isMouseBtnReleased(MouseBtn btn) const; + /** + * @brief 获取鼠标位置(浮点) + */ + void getMousePos(float& x, float& y) const; - /** - * @brief 获取鼠标滚轮 - */ - int32 getMouseWheel() const; + /** + * @brief 检查鼠标按键是否按下 + */ + bool isMouseBtnDown(MouseBtn btn) const; - // ========== 触摸 ========== + /** + * @brief 检查鼠标按键是否刚按下 + */ + bool isMouseBtnPressed(MouseBtn btn) const; - /** - * @brief 获取触摸点数量 - */ - int32 touchCount() const; + /** + * @brief 检查鼠标按键是否刚释放 + */ + bool isMouseBtnReleased(MouseBtn btn) const; - /** - * @brief 获取触摸点 - * @param idx 触摸点索引 - * @return 触摸点信息,无效索引返回 nullptr - */ - const TouchPoint *getTouch(int32 idx) const; + /** + * @brief 获取鼠标滚轮 + */ + int32 getMouseWheel() const; - /** - * @brief 根据 ID 获取触摸点 - */ - const TouchPoint *getTouchById(int64 id) const; + // ========== 触摸 ========== - /** - * @brief 检查是否有触摸 - */ - bool hasTouch() const { return touchCount() > 0; } + /** + * @brief 获取触摸点数量 + */ + int32 touchCount() const; - /** - * @brief 获取所有活跃触摸点 - */ - const std::vector &getTouches() const { return activeTouches_; } + /** + * @brief 获取触摸点 + * @param idx 触摸点索引 + * @return 触摸点信息,无效索引返回 nullptr + */ + const TouchPoint* getTouch(int32 idx) const; - // ========== 游戏手柄 ========== + /** + * @brief 根据 ID 获取触摸点 + */ + const TouchPoint* getTouchById(int64 id) const; - /** - * @brief 连接的游戏手柄数量 - */ - int32 gamepadCount() const; + /** + * @brief 检查是否有触摸 + */ + bool hasTouch() const { return touchCount() > 0; } - /** - * @brief 检查手柄按键是否按下 - */ - bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const; + /** + * @brief 获取所有活跃触摸点 + */ + const std::vector& getTouches() const { return activeTouches_; } - /** - * @brief 检查手柄按键是否刚按下 - */ - bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const; + // ========== 游戏手柄 ========== - /** - * @brief 获取手柄轴值 (-1.0 到 1.0) - */ - float getGamepadAxis(int32 idx, GamepadAxis axis) const; + /** + * @brief 连接的游戏手柄数量 + */ + int32 gamepadCount() const; - // ========== 回调设置 ========== + /** + * @brief 检查手柄按键是否按下 + */ + bool isGamepadBtnDown(int32 idx, GamepadBtn btn) const; - /** - * @brief 设置按键按下回调 - */ - void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); } + /** + * @brief 检查手柄按键是否刚按下 + */ + bool isGamepadBtnPressed(int32 idx, GamepadBtn btn) const; - /** - * @brief 设置按键释放回调 - */ - void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); } + /** + * @brief 获取手柄轴值 (-1.0 到 1.0) + */ + float getGamepadAxis(int32 idx, GamepadAxis axis) const; - /** - * @brief 设置鼠标按下回调 - */ - void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); } + // ========== 回调设置 ========== - /** - * @brief 设置鼠标释放回调 - */ - void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); } + /** + * @brief 设置按键按下回调 + */ + void setOnKeyDown(KeyCb cb) { onKeyDown_ = std::move(cb); } - /** - * @brief 设置触摸开始回调 - */ - void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); } + /** + * @brief 设置按键释放回调 + */ + void setOnKeyUp(KeyCb cb) { onKeyUp_ = std::move(cb); } - /** - * @brief 设置触摸移动回调 - */ - void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); } + /** + * @brief 设置鼠标按下回调 + */ + void setOnMouseDown(MouseBtnCb cb) { onMouseDown_ = std::move(cb); } - /** - * @brief 设置触摸结束回调 - */ - void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); } + /** + * @brief 设置鼠标释放回调 + */ + void setOnMouseUp(MouseBtnCb cb) { onMouseUp_ = std::move(cb); } + + /** + * @brief 设置触摸开始回调 + */ + void setOnTouchBegan(TouchCb cb) { onTouchBegan_ = std::move(cb); } + + /** + * @brief 设置触摸移动回调 + */ + void setOnTouchMoved(TouchCb cb) { onTouchMoved_ = std::move(cb); } + + /** + * @brief 设置触摸结束回调 + */ + void setOnTouchEnded(TouchCb cb) { onTouchEnded_ = std::move(cb); } private: - InputSvc() = default; + static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES; + static constexpr int32 MAX_GAMEPADS = 4; + static constexpr int32 MAX_TOUCHES = 10; - static constexpr int32 KEY_COUNT = SDL_NUM_SCANCODES; - static constexpr int32 MAX_GAMEPADS = 4; - static constexpr int32 MAX_TOUCHES = 10; + std::array keyState_{}; + std::array keyPrev_{}; - std::array keyState_{}; - std::array keyPrev_{}; + int32 mouseX_ = 0; + int32 mouseY_ = 0; + int32 mouseWheel_ = 0; + std::array(MouseBtn::Count)> mouseState_{}; + std::array(MouseBtn::Count)> mousePrev_{}; - int32 mouseX_ = 0; - int32 mouseY_ = 0; - int32 mouseWheel_ = 0; - std::array(MouseBtn::Count)> mouseState_{}; - std::array(MouseBtn::Count)> mousePrev_{}; + std::vector activeTouches_; + std::vector endedTouches_; - std::vector activeTouches_; - std::vector endedTouches_; + SDL_GameController* gamepads_[MAX_GAMEPADS] = {}; + std::array(GamepadBtn::Count)> padState_[MAX_GAMEPADS]; + std::array(GamepadBtn::Count)> padPrev_[MAX_GAMEPADS]; - SDL_GameController *gamepads_[MAX_GAMEPADS] = {}; - std::array(GamepadBtn::Count)> - padState_[MAX_GAMEPADS]; - std::array(GamepadBtn::Count)> - padPrev_[MAX_GAMEPADS]; + KeyCb onKeyDown_; + KeyCb onKeyUp_; + MouseBtnCb onMouseDown_; + MouseBtnCb onMouseUp_; + TouchCb onTouchBegan_; + TouchCb onTouchMoved_; + TouchCb onTouchEnded_; - KeyCb onKeyDown_; - KeyCb onKeyUp_; - MouseBtnCb onMouseDown_; - MouseBtnCb onMouseUp_; - TouchCb onTouchBegan_; - TouchCb onTouchMoved_; - TouchCb onTouchEnded_; + void openGamepad(int32 idx); + void closeGamepad(int32 idx); - void processEvent(const SDL_Event &evt); - void openGamepad(int32 idx); - void closeGamepad(int32 idx); - - void processTouchDown(const SDL_TouchFingerEvent &evt); - void processTouchUp(const SDL_TouchFingerEvent &evt); - void processTouchMotion(const SDL_TouchFingerEvent &evt); - - friend class WindowSvc; + void processTouchDown(const SDL_TouchFingerEvent& evt); + void processTouchUp(const SDL_TouchFingerEvent& evt); + void processTouchMotion(const SDL_TouchFingerEvent& evt); }; -#define INPUT_SVC extra2d::InputSvc::inst() - } // namespace extra2d diff --git a/include/platform/window.h b/include/platform/window.h index 0d79011..005ab6b 100644 --- a/include/platform/window.h +++ b/include/platform/window.h @@ -1,12 +1,13 @@ #pragma once #include -#include +#include +#include #include #include +#include #include #include -#include namespace extra2d { @@ -14,148 +15,154 @@ namespace extra2d { * @brief 窗口配置 */ struct WindowCfg { - std::string title = "Extra2D"; - int32 width = 1280; - int32 height = 720; - bool fullscreen = false; - bool resizable = true; - bool vsync = true; - int32 glMajor = 3; - int32 glMinor = 3; + std::string title = "Extra2D"; + int32 width = 1280; + int32 height = 720; + bool fullscreen = false; + bool resizable = true; + bool vsync = true; + int32 glMajor = 3; + int32 glMinor = 3; }; /** * @brief 窗口事件回调 */ -using ResizeCb = Fn; -using CloseCb = Fn; +using ResizeCb = std::function; +using CloseCb = std::function; /** - * @brief 窗口服务 + * @brief 窗口模块 * * 管理 SDL2 窗口和 OpenGL 上下文 + * 非单例设计,通过 Context 管理生命周期 */ -class WindowSvc : public IService { +class WindowModule : public IModule { public: - /** - * @brief 获取单例实例 - */ - static WindowSvc &inst(); + WindowModule(); + ~WindowModule() override; - const char *name() const override { return "WindowSvc"; } - int pri() const override { return Pri::System; } + // 禁止拷贝 + WindowModule(const WindowModule&) = delete; + WindowModule& operator=(const WindowModule&) = delete; - bool init() override; - void shutdown() override; + // 允许移动 + WindowModule(WindowModule&&) noexcept; + WindowModule& operator=(WindowModule&&) noexcept; - /** - * @brief 使用配置创建窗口 - */ - bool create(const WindowCfg &cfg); + // IModule 接口实现 + const char* name() const override { return "Window"; } + ModuleType type() const override { return ModuleType::System; } + int priority() const override { return Pri::Window; } + bool init() override; + void shutdown() override; - /** - * @brief 处理窗口事件 - * @return true 继续运行,false 应退出 - */ - bool pollEvents(); + /** + * @brief 使用配置创建窗口 + */ + bool create(const WindowCfg& cfg); - /** - * @brief 交换缓冲区 - */ - void swapBuffers(); + /** + * @brief 处理窗口事件 + * @return true 继续运行,false 应退出 + */ + bool pollEvents(); - /** - * @brief 获取 SDL 窗口句柄 - */ - SDL_Window *handle() const { return window_; } + /** + * @brief 交换缓冲区 + */ + void swapBuffers(); - /** - * @brief 获取 OpenGL 上下文 - */ - SDL_GLContext glContext() const { return glCtx_; } + /** + * @brief 获取 SDL 窗口句柄 + */ + SDL_Window* handle() const { return window_; } - /** - * @brief 获取窗口尺寸 - */ - Size getSize() const; + /** + * @brief 获取 OpenGL 上下文 + */ + SDL_GLContext glContext() const { return glCtx_; } - /** - * @brief 获取窗口位置 - */ - Vec2 getPosition() const; + /** + * @brief 获取窗口尺寸 + */ + Size getSize() const; - /** - * @brief 设置窗口尺寸 - */ - void setSize(int32 w, int32 h); + /** + * @brief 获取窗口位置 + */ + Vec2 getPosition() const; - /** - * @brief 设置窗口标题 - */ - void setTitle(const std::string &title); + /** + * @brief 设置窗口尺寸 + */ + void setSize(int32 w, int32 h); - /** - * @brief 设置全屏模式 - */ - void setFullscreen(bool fullscreen); + /** + * @brief 设置窗口标题 + */ + void setTitle(const std::string& title); - /** - * @brief 检查是否全屏 - */ - bool isFullscreen() const; + /** + * @brief 设置全屏模式 + */ + void setFullscreen(bool fullscreen); - /** - * @brief 设置垂直同步 - */ - void setVsync(bool vsync); + /** + * @brief 检查是否全屏 + */ + bool isFullscreen() const; - /** - * @brief 检查是否垂直同步 - */ - bool isVsync() const; + /** + * @brief 设置垂直同步 + */ + void setVsync(bool vsync); - /** - * @brief 显示/隐藏窗口 - */ - void setVisible(bool visible); + /** + * @brief 检查是否垂直同步 + */ + bool isVsync() const; - /** - * @brief 检查窗口是否可见 - */ - bool isVisible() const; + /** + * @brief 显示/隐藏窗口 + */ + void setVisible(bool visible); - /** - * @brief 设置窗口关闭回调 - */ - void setOnClose(CloseCb cb) { onClose_ = std::move(cb); } + /** + * @brief 检查窗口是否可见 + */ + bool isVisible() const; - /** - * @brief 设置窗口大小改变回调 - */ - void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); } + /** + * @brief 设置窗口关闭回调 + */ + void setOnClose(CloseCb cb) { onClose_ = std::move(cb); } - /** - * @brief 请求关闭窗口 - */ - void requestClose() { shouldClose_ = true; } + /** + * @brief 设置窗口大小改变回调 + */ + void setOnResize(ResizeCb cb) { onResize_ = std::move(cb); } - /** - * @brief 检查是否应该关闭 - */ - bool shouldClose() const { return shouldClose_; } + /** + * @brief 请求关闭窗口 + */ + void requestClose() { shouldClose_ = true; } + + /** + * @brief 检查是否应该关闭 + */ + bool shouldClose() const { return shouldClose_; } private: - WindowSvc() = default; + void handleWindowEvent(const SDL_WindowEvent& evt); - SDL_Window *window_ = nullptr; - SDL_GLContext glCtx_ = nullptr; - bool shouldClose_ = false; - bool vsync_ = true; + SDL_Window* window_ = nullptr; + SDL_GLContext glCtx_ = nullptr; + bool shouldClose_ = false; + bool vsync_ = true; - CloseCb onClose_; - ResizeCb onResize_; + CloseCb onClose_; + ResizeCb onResize_; }; -#define WINDOW extra2d::WindowSvc::inst() - } // namespace extra2d diff --git a/include/plugin/iplugin.h b/include/plugin/iplugin.h new file mode 100644 index 0000000..dd02187 --- /dev/null +++ b/include/plugin/iplugin.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 插件信息结构 + */ +struct PluginInfo { + std::string name; // 插件名称 + std::string version; // 版本号 + std::string author; // 作者 + std::string description; // 描述 + std::vector dependencies; // 依赖的插件名称列表 +}; + +/** + * @brief 插件接口 - 基于事件总线 + * + * 插件是动态加载的扩展组件,可以在运行时加载/卸载 + * 插件通过事件总线与其他组件通信,零直接依赖 + * + * 设计理念: + * - 插件可以在运行时动态加载(动态库)或静态链接 + * - 插件之间可以声明依赖关系 + * - 插件通过事件总线发送和接收消息 + */ +class IPlugin { +public: + virtual ~IPlugin() = default; + + /** + * @brief 获取插件信息 + * @return 插件信息结构 + */ + virtual const PluginInfo& getInfo() const = 0; + + /** + * @brief 加载插件 + * + * 在此注册事件监听器 + * 返回 false 表示加载失败 + * + * @return 加载是否成功 + */ + virtual bool load() { return true; } + + /** + * @brief 卸载插件 + * + * 在此执行清理工作 + * 事件监听器会自动注销(RAII) + */ + virtual void unload() {} + + /** + * @brief 获取依赖的插件列表 + * @return 依赖的插件名称列表 + */ + virtual std::vector getDependencies() const { + return getInfo().dependencies; + } +}; + +} // namespace extra2d + +// ============================================================================ +// 插件导出宏(用于动态库插件) +// ============================================================================ + +#ifdef _WIN32 + #define EXTRA2D_PLUGIN_EXPORT extern "C" __declspec(dllexport) +#else + #define EXTRA2D_PLUGIN_EXPORT extern "C" __attribute__((visibility("default"))) +#endif + +/** + * @brief 定义插件导出接口 + * + * 在插件动态库中使用: + * EXTRA2D_DEFINE_PLUGIN(MyPluginClass) + */ +#define EXTRA2D_DEFINE_PLUGIN(PluginClass) \ + EXTRA2D_PLUGIN_EXPORT extra2d::IPlugin* extra2d_create_plugin() { \ + return new PluginClass(); \ + } \ + EXTRA2D_PLUGIN_EXPORT void extra2d_destroy_plugin(extra2d::IPlugin* plugin) { \ + delete plugin; \ + } diff --git a/include/plugin/plugin_loader.h b/include/plugin/plugin_loader.h new file mode 100644 index 0000000..aef72b1 --- /dev/null +++ b/include/plugin/plugin_loader.h @@ -0,0 +1,156 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 插件加载器 - 非单例 + * + * 管理所有插件的生命周期,支持动态加载/卸载 + * 自动处理插件依赖关系 + */ +class PluginLoader { +public: + PluginLoader(); + ~PluginLoader(); + + // 禁止拷贝 + PluginLoader(const PluginLoader&) = delete; + PluginLoader& operator=(const PluginLoader&) = delete; + + // 允许移动 + PluginLoader(PluginLoader&&) noexcept; + PluginLoader& operator=(PluginLoader&&) noexcept; + + /** + * @brief 从动态库加载插件 + * @param path 插件动态库路径 + * @return 是否加载成功 + */ + bool loadFromLibrary(const char* path); + + /** + * @brief 注册内置插件(静态链接) + * @param plugin 插件实例指针(不由加载器管理生命周期) + */ + void registerPlugin(IPlugin* plugin); + + /** + * @brief 卸载插件 + * @param name 插件名称 + */ + void unloadPlugin(const char* name); + + /** + * @brief 获取插件 + * @param name 插件名称 + * @return 插件指针,不存在返回 nullptr + */ + IPlugin* getPlugin(const char* name) const; + + /** + * @brief 检查插件是否存在 + * @param name 插件名称 + * @return 是否存在 + */ + bool hasPlugin(const char* name) const; + + /** + * @brief 获取所有插件 + * @return 插件列表 + */ + std::vector getAllPlugins() const; + + /** + * @brief 初始化所有插件(自动处理依赖) + * @return 是否全部初始化成功 + */ + bool initAll(); + + /** + * @brief 关闭所有插件(按依赖逆序) + */ + void shutdownAll(); + + /** + * @brief 获取插件数量 + * @return 插件数量 + */ + size_t getPluginCount() const; + + /** + * @brief 添加插件搜索路径 + * @param path 搜索路径 + */ + void addSearchPath(const char* path); + + /** + * @brief 从目录加载所有插件 + * @param directory 目录路径 + * @return 加载成功的插件数量 + */ + size_t loadPluginsFromDirectory(const char* directory); + +private: + /** + * @brief 插件条目结构 + */ + struct PluginEntry { + IPlugin* plugin = nullptr; // 插件实例 + void* handle = nullptr; // 动态库句柄 + bool isDynamic = false; // 是否为动态加载 + bool owned = false; // 是否由加载器管理生命周期 + }; + + /** + * @brief 解析插件依赖 + * @param plugin 插件 + * @return 依赖是否满足 + */ + bool resolveDependencies(IPlugin* plugin); + + /** + * @brief 检查依赖是否满足 + * @param dependencies 依赖列表 + * @return 是否满足 + */ + bool checkDependencies(const std::vector& dependencies); + + /** + * @brief 按依赖顺序排序插件 + */ + void sortPluginsByDependencies(); + + /** + * @brief 加载动态库 + * @param path 库路径 + * @return 库句柄 + */ + void* loadDynamicLibrary(const char* path); + + /** + * @brief 卸载动态库 + * @param handle 库句柄 + */ + void unloadDynamicLibrary(void* handle); + + /** + * @brief 获取动态库符号 + * @param handle 库句柄 + * @param name 符号名称 + * @return 符号地址 + */ + void* getSymbol(void* handle, const char* name); + + std::unordered_map plugins_; + std::vector searchPaths_; + std::vector sortedPlugins_; // 按依赖排序的插件列表 + bool inited_ = false; +}; + +} // namespace extra2d diff --git a/include/types/const/priority.h b/include/types/const/priority.h index 6ec699f..018a12a 100644 --- a/include/types/const/priority.h +++ b/include/types/const/priority.h @@ -5,10 +5,17 @@ namespace extra2d { +/** + * @brief 模块优先级常量 + * + * 数值越小优先级越高,越早初始化,越晚关闭 + */ namespace Pri { constexpr int32 Min = INT32_MIN; constexpr int32 System = -1000; - constexpr int32 Input = -100; + constexpr int32 Window = -900; // 窗口模块(最早初始化) + constexpr int32 Input = -800; // 输入模块 + constexpr int32 File = -700; // 文件模块 constexpr int32 Default = 0; constexpr int32 Logic = 100; constexpr int32 Anim = 200; diff --git a/include/types/ptr/intrusive_ptr.h b/include/types/ptr/intrusive_ptr.h index 098d12b..7466de6 100644 --- a/include/types/ptr/intrusive_ptr.h +++ b/include/types/ptr/intrusive_ptr.h @@ -25,6 +25,7 @@ public: Ptr& operator=(T* p) { reset(p); return *this; } Ptr& operator=(const Ptr& r) { return *this = r.p_; } + template Ptr& operator=(const Ptr& r) { return *this = r.get(); } Ptr& operator=(Ptr&& r) noexcept { Ptr(std::move(r)).swap(*this); return *this; } bool operator==(std::nullptr_t) const { return p_ == nullptr; } diff --git a/include/utils/random.h b/include/utils/random.h index ca798b7..30c97e2 100644 --- a/include/utils/random.h +++ b/include/utils/random.h @@ -3,84 +3,75 @@ #include #include - namespace extra2d { // ============================================================================ // Random 类 - 随机数生成器 +// 非单例设计,可以创建多个独立的随机数生成器实例 // ============================================================================ class Random { public: - /// 获取单例实例 - static Random &getInstance(); + /// 默认构造函数(使用随机种子) + Random(); - /// 设置随机种子 - void setSeed(uint32 seed); + /// 使用指定种子构造 + explicit Random(uint32 seed); - /// 使用当前时间作为种子 - void randomize(); + /// 设置随机种子 + void setSeed(uint32 seed); - /// 获取 [0, 1) 范围内的随机浮点数 - float getFloat(); + /// 使用当前时间作为种子 + void randomize(); - /// 获取 [min, max] 范围内的随机浮点数 - float getFloat(float min, float max); + /// 获取 [0, 1) 范围内的随机浮点数 + float getFloat(); - /// 获取 [0, max] 范围内的随机整数 - int getInt(int max); + /// 获取 [min, max] 范围内的随机浮点数 + float getFloat(float min, float max); - /// 获取 [min, max] 范围内的随机整数 - int getInt(int min, int max); + /// 获取 [0, max] 范围内的随机整数 + int getInt(int max); - /// 获取随机布尔值 - bool getBool(); + /// 获取 [min, max] 范围内的随机整数 + int getInt(int min, int max); - /// 获取随机布尔值(带概率) - bool getBool(float probability); + /// 获取随机布尔值 + bool getBool(); - /// 获取指定范围内的随机角度(弧度) - float getAngle(); + /// 获取随机布尔值(带概率) + bool getBool(float probability); - /// 获取 [-1, 1] 范围内的随机数(用于方向) - float getSigned(); + /// 获取指定范围内的随机角度(弧度) + float getAngle(); + + /// 获取 [-1, 1] 范围内的随机数(用于方向) + float getSigned(); private: - Random(); - ~Random() = default; - - Random(const Random &) = delete; - Random &operator=(const Random &) = delete; - - std::mt19937 generator_; - std::uniform_real_distribution floatDist_; + std::mt19937 generator_; + std::uniform_real_distribution floatDist_; }; // ============================================================================ -// 便捷函数 +// 便捷函数 - 使用全局默认随机数生成器 // ============================================================================ /// 获取 [0, 1) 范围内的随机浮点数 -inline float randomFloat() { return Random::getInstance().getFloat(); } +float randomFloat(); /// 获取 [min, max] 范围内的随机浮点数 -inline float randomFloat(float min, float max) { - return Random::getInstance().getFloat(min, max); -} +float randomFloat(float min, float max); /// 获取 [0, max] 范围内的随机整数 -inline int randomInt(int max) { return Random::getInstance().getInt(max); } +int randomInt(int max); /// 获取 [min, max] 范围内的随机整数 -inline int randomInt(int min, int max) { - return Random::getInstance().getInt(min, max); -} +int randomInt(int min, int max); /// 获取随机布尔值 -inline bool randomBool() { return Random::getInstance().getBool(); } +bool randomBool(); /// 获取随机布尔值(带概率) -inline bool randomBool(float probability) { - return Random::getInstance().getBool(probability); -} +bool randomBool(float probability); } // namespace extra2d diff --git a/src/app/application.cpp b/src/app/application.cpp index c291a46..73c1cd1 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -1,14 +1,9 @@ #include -#include -#include +#include #include -#include -#include #include -#include #include - #include #include @@ -35,12 +30,18 @@ static double getTimeSeconds() { #endif } -Application &Application::instance() { - static Application instance; - return instance; +std::unique_ptr Application::create() { + return std::unique_ptr(new Application()); } -Application::~Application() { shutdown(); } +Application::Application() = default; + +Application::~Application() { + shutdown(); +} + +Application::Application(Application&&) noexcept = default; +Application& Application::operator=(Application&&) noexcept = default; bool Application::init(const AppConfig &config) { if (initialized_) { @@ -70,38 +71,19 @@ bool Application::init(const AppConfig &config) { return false; } - SVC_MGR.reg(&WINDOW); - SVC_MGR.reg(&INPUT_SVC); - SVC_MGR.reg(&FILE_SVC); - - if (!SVC_MGR.initAll()) { - E2D_LOG_ERROR("Failed to initialize services"); + // 创建引擎上下文 + context_ = Context::create(); + if (!context_) { + E2D_LOG_ERROR("Failed to create context"); return false; } - if (!WINDOW.create([&] { - WindowCfg cfg; - cfg.title = config_.title; - cfg.width = config_.width; - cfg.height = config_.height; - cfg.fullscreen = config_.fullscreen; - cfg.resizable = config_.resizable; - cfg.vsync = config_.vsync; - cfg.glMajor = config_.glMajor; - cfg.glMinor = config_.glMinor; - return cfg; - }())) { - E2D_LOG_ERROR("Failed to create window"); + // 初始化引擎 + if (!context_->init()) { + E2D_LOG_ERROR("Failed to initialize context"); return false; } - if (!DIRECTOR.init()) { - E2D_LOG_ERROR("Failed to initialize Director"); - return false; - } - - WINDOW.setOnClose([this]() { quit(); }); - initialized_ = true; running_ = true; @@ -121,8 +103,9 @@ void Application::shutdown() { E2D_LOG_INFO("Shutting down application..."); - DIRECTOR.shutdown(); - SVC_MGR.shutdownAll(); + // 关闭上下文(会自动关闭模块和插件) + context_.reset(); + Sdl2::shutdown(); #ifdef __SWITCH__ @@ -144,7 +127,7 @@ void Application::run() { lastFrameTime_ = getTimeSeconds(); - while (running_ && !WINDOW.shouldClose()) { + while (running_) { mainLoop(); } } @@ -152,14 +135,11 @@ void Application::run() { void Application::quit() { shouldQuit_ = true; running_ = false; - WINDOW.requestClose(); } void Application::pause() { if (!paused_) { paused_ = true; - SVC_MGR.pauseAll(); - DIRECTOR.pause(); events::OnPause::emit(); E2D_LOG_INFO("Application paused"); } @@ -168,20 +148,13 @@ void Application::pause() { void Application::resume() { if (paused_) { paused_ = false; - SVC_MGR.resumeAll(); - DIRECTOR.resume(); - lastFrameTime_ = getTimeSeconds(); events::OnResume::emit(); + lastFrameTime_ = getTimeSeconds(); E2D_LOG_INFO("Application resumed"); } } void Application::mainLoop() { - if (!WINDOW.pollEvents()) { - running_ = false; - return; - } - double currentTime = getTimeSeconds(); deltaTime_ = static_cast(currentTime - lastFrameTime_); lastFrameTime_ = currentTime; @@ -200,8 +173,7 @@ void Application::mainLoop() { update(); } - WINDOW.swapBuffers(); - + // 帧率限制 if (!config_.vsync && config_.fpsLimit > 0) { double frameEndTime = getTimeSeconds(); double frameTime = frameEndTime - currentTime; @@ -214,9 +186,10 @@ void Application::mainLoop() { } void Application::update() { - SVC_MGR.updateAll(deltaTime_); - events::OnUpdate::emit(deltaTime_); - DIRECTOR.mainLoop(deltaTime_); + // 通过上下文更新引擎 + if (context_) { + context_->tick(deltaTime_); + } } } // namespace extra2d diff --git a/src/context/context.cpp b/src/context/context.cpp new file mode 100644 index 0000000..0d04e85 --- /dev/null +++ b/src/context/context.cpp @@ -0,0 +1,106 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +Context::Context() + : moduleRegistry_(std::make_unique()) + , pluginLoader_(std::make_unique()) + , timerModule_(std::make_unique()) +{ +} + +Context::~Context() { + if (inited_) { + shutdown(); + } +} + +Context::Context(Context&&) noexcept = default; +Context& Context::operator=(Context&&) noexcept = default; + +std::unique_ptr Context::create() { + return std::make_unique(); +} + +bool Context::init() { + if (inited_) { + return true; + } + + // 发送引擎初始化事件 + events::OnInit::emit(); + + // 注册核心模块 + moduleRegistry_->registerModule(timerModule_.get()); + + // 初始化所有模块 + if (!moduleRegistry_->initAll()) { + return false; + } + + // 初始化所有插件 + if (!pluginLoader_->initAll()) { + return false; + } + + inited_ = true; + running_ = true; + return true; +} + +void Context::shutdown() { + if (!inited_) { + return; + } + + running_ = false; + + // 关闭所有插件 + pluginLoader_->shutdownAll(); + + // 关闭所有模块 + moduleRegistry_->shutdownAll(); + + // 发送引擎关闭事件 + events::OnShutdown::emit(); + + inited_ = false; +} + +void Context::tick(float dt) { + if (!running_) { + return; + } + + // 更新时间和帧数 + totalTime_ += dt; + frameCount_++; + + // 更新定时器模块 + timerModule_->update(dt); + + // 发送更新事件 + events::OnUpdate::emit(dt); + + // 发送延迟更新事件 + events::OnLateUpdate::emit(dt); +} + +ModuleRegistry& Context::modules() { + return *moduleRegistry_; +} + +PluginLoader& Context::plugins() { + return *pluginLoader_; +} + +TimerModule& Context::timer() { + return *timerModule_; +} + +} // namespace extra2d diff --git a/src/core/director.cpp b/src/core/director.cpp deleted file mode 100644 index ab37340..0000000 --- a/src/core/director.cpp +++ /dev/null @@ -1,78 +0,0 @@ -#include - -namespace extra2d { - -Director& Director::inst() { - static Director instance; - return instance; -} - -bool Director::init() { - if (inited_) return true; - - inited_ = true; - return true; -} - -void Director::shutdown() { - if (!inited_) return; - - SVC_MGR.shutdownAll(); - SCHED.unscheduleAll(); - - inited_ = false; -} - -void Director::mainLoop(float dt) { - if (paused_) return; - - dt_ = dt; - totalTime_ += dt; - frameCount_++; - - SVC_MGR.updateAll(dt); - - fixedAccumulator_ += dt; - while (fixedAccumulator_ >= fixedDt_) { - SVC_MGR.fixedUpdateAll(fixedDt_); - fixedAccumulator_ -= fixedDt_; - } - - SCHED.update(dt); - - SVC_MGR.lateUpdateAll(dt); -} - -void Director::mainLoopParallel(float dt) { - if (paused_) return; - - dt_ = dt; - totalTime_ += dt; - frameCount_++; - - SVC_MGR.updateAll(dt); - - fixedAccumulator_ += dt; - while (fixedAccumulator_ >= fixedDt_) { - SVC_MGR.fixedUpdateAll(fixedDt_); - fixedAccumulator_ -= fixedDt_; - } - - SCHED.updateParallel(dt); - - SVC_MGR.lateUpdateAll(dt); -} - -void Director::pause() { - paused_ = true; -} - -void Director::resume() { - paused_ = false; -} - -void Director::setTimeScale(float scale) { - SCHED.setTimeScale(scale); -} - -} // namespace extra2d diff --git a/src/core/scheduler.cpp b/src/core/scheduler.cpp deleted file mode 100644 index c8937cf..0000000 --- a/src/core/scheduler.cpp +++ /dev/null @@ -1,322 +0,0 @@ -#include -#include -#include -#include - -namespace extra2d { - -namespace { - -class IntervalTimer : public Timer { -public: - IntervalTimer(Scheduler::Cb cb, float interval, uint32 repeat, float delay) - : cb_(std::move(cb)) { - interval_ = interval; - repeat_ = repeat; - delay_ = delay; - useDelay_ = delay > 0.0f; - runForever_ = repeat == 0; - elapsed_ = useDelay_ ? 0.0f : -interval_; - } - - void update(float dt) override { - if (paused_ || done_) return; - - elapsed_ += dt; - - if (useDelay_) { - if (elapsed_ < delay_) return; - elapsed_ -= delay_; - useDelay_ = false; - } - - if (elapsed_ >= interval_) { - trigger(); - elapsed_ -= interval_; - if (!runForever_) { - timesExecuted_++; - if (timesExecuted_ >= repeat_) { - done_ = true; - } - } - } - } - - void trigger() override { - if (cb_) cb_(elapsed_); - } - -private: - Scheduler::Cb cb_; -}; - -class OnceTimer : public Timer { -public: - OnceTimer(Scheduler::VoidCb cb, float delay) - : cb_(std::move(cb)) { - delay_ = delay; - elapsed_ = 0.0f; - } - - void update(float dt) override { - if (paused_ || done_) return; - - elapsed_ += dt; - if (elapsed_ >= delay_) { - trigger(); - done_ = true; - } - } - - void trigger() override { - if (cb_) cb_(); - } - -private: - Scheduler::VoidCb cb_; -}; - -} - -// SafePriorityQueue 实现 -void Scheduler::SafePriorityQueue::push(const UpdateEntry& entry) { - std::lock_guard lock(mutex_); - queue_.push(entry); -} - -bool Scheduler::SafePriorityQueue::pop(UpdateEntry& entry) { - std::lock_guard lock(mutex_); - if (queue_.empty()) return false; - entry = queue_.top(); - queue_.pop(); - return true; -} - -bool Scheduler::SafePriorityQueue::empty() const { - std::lock_guard lock(mutex_); - return queue_.empty(); -} - -size_t Scheduler::SafePriorityQueue::size() const { - std::lock_guard lock(mutex_); - return queue_.size(); -} - -void Scheduler::SafePriorityQueue::clear() { - std::lock_guard lock(mutex_); - while (!queue_.empty()) queue_.pop(); -} - -Scheduler& Scheduler::inst() { - static Scheduler instance; - return instance; -} - -TimerHdl Scheduler::scheduleUpdate(TimerTarget* target, int pri) { - if (!target) return INVALID_HDL; - - std::lock_guard lock(updateIndexMutex_); - UpdateEntry entry{target, pri, false, false}; - updates_.push_back(entry); - updateIndex_[target] = updates_.size() - 1; - - return genHdl(); -} - -void Scheduler::unscheduleUpdate(TimerTarget* target) { - if (!target) return; - - std::lock_guard lock(updateIndexMutex_); - auto it = updateIndex_.find(target); - if (it != updateIndex_.end()) { - size_t idx = it->second; - if (idx < updates_.size()) { - updates_[idx].markedForDel = true; - } - updateIndex_.erase(it); - } -} - -TimerHdl Scheduler::schedule(Cb cb, float interval, uint32 repeat, float delay) { - if (!cb) return INVALID_HDL; - - auto timer = makePtr(std::move(cb), interval, repeat, delay); - TimerHdl hdl = genHdl(); - timer->hdl_ = hdl; - - { - std::lock_guard lock(timersMutex_); - timers_[hdl] = timer; - } - return hdl; -} - -TimerHdl Scheduler::scheduleOnce(VoidCb cb, float delay) { - if (!cb) return INVALID_HDL; - - auto timer = makePtr(std::move(cb), delay); - TimerHdl hdl = genHdl(); - timer->hdl_ = hdl; - - { - std::lock_guard lock(timersMutex_); - timers_[hdl] = timer; - } - return hdl; -} - -TimerHdl Scheduler::scheduleForever(Cb cb, float interval) { - return schedule(std::move(cb), interval, 0, 0.0f); -} - -void Scheduler::unschedule(TimerHdl hdl) { - std::lock_guard lock(timersMutex_); - timers_.erase(hdl); -} - -void Scheduler::unscheduleAll() { - { - std::lock_guard lock(timersMutex_); - timers_.clear(); - } - { - std::lock_guard lock(updateIndexMutex_); - updates_.clear(); - updateIndex_.clear(); - } - updateQueue_.clear(); -} - -void Scheduler::pause(TimerHdl hdl) { - std::lock_guard lock(timersMutex_); - auto it = timers_.find(hdl); - if (it != timers_.end()) { - it->second->pause(); - } -} - -void Scheduler::resume(TimerHdl hdl) { - std::lock_guard lock(timersMutex_); - auto it = timers_.find(hdl); - if (it != timers_.end()) { - it->second->resume(); - } -} - -void Scheduler::update(float dt) { - float scaledDt = dt * timeScale_.load(); - - locked_ = true; - - // 更新所有 update 回调 - { - std::lock_guard lock(updateIndexMutex_); - for (auto& entry : updates_) { - if (!entry.markedForDel && !entry.paused && entry.target) { - entry.target->update(scaledDt); - } - } - - updates_.erase( - std::remove_if(updates_.begin(), updates_.end(), - [](const UpdateEntry& e) { return e.markedForDel; }), - updates_.end() - ); - } - - // 更新定时器 - std::vector toRemove; - { - std::lock_guard lock(timersMutex_); - for (auto it = timers_.begin(); it != timers_.end(); ++it) { - auto& timer = it->second; - timer->update(scaledDt); - if (timer->isDone()) { - toRemove.push_back(it->first); - } - } - - for (auto hdl : toRemove) { - timers_.erase(hdl); - } - } - - locked_ = false; -} - -void Scheduler::updateParallel(float dt) { - float scaledDt = dt * timeScale_.load(); - - locked_ = true; - - // 并行更新所有 update 回调(使用标准库线程) - { - std::lock_guard lock(updateIndexMutex_); - size_t numThreads = std::thread::hardware_concurrency(); - if (numThreads == 0) numThreads = 4; - - size_t batchSize = updates_.size() / numThreads; - if (batchSize == 0) batchSize = 1; - - std::vector> futures; - - for (size_t t = 0; t < numThreads && t * batchSize < updates_.size(); ++t) { - size_t start = t * batchSize; - size_t end = (t == numThreads - 1) ? updates_.size() : (t + 1) * batchSize; - - futures.push_back(std::async(std::launch::async, [this, start, end, scaledDt]() { - for (size_t i = start; i < end; ++i) { - auto& entry = updates_[i]; - if (!entry.markedForDel && !entry.paused && entry.target) { - entry.target->update(scaledDt); - } - } - })); - } - - for (auto& f : futures) { - f.wait(); - } - - updates_.erase( - std::remove_if(updates_.begin(), updates_.end(), - [](const UpdateEntry& e) { return e.markedForDel; }), - updates_.end() - ); - } - - // 更新定时器 - std::vector toRemove; - { - std::lock_guard lock(timersMutex_); - for (auto it = timers_.begin(); it != timers_.end(); ++it) { - auto& timer = it->second; - timer->update(scaledDt); - if (timer->isDone()) { - toRemove.push_back(it->first); - } - } - - for (auto hdl : toRemove) { - timers_.erase(hdl); - } - } - - locked_ = false; -} - -bool Scheduler::isScheduled(TimerHdl hdl) const { - std::lock_guard lock(timersMutex_); - return timers_.find(hdl) != timers_.end(); -} - -size_t Scheduler::count() const { - std::lock_guard lock(timersMutex_); - return timers_.size(); -} - -TimerHdl Scheduler::genHdl() { - return nextHdl_.fetch_add(1, std::memory_order_relaxed); -} - -} // namespace extra2d diff --git a/src/core/service.cpp b/src/core/service.cpp deleted file mode 100644 index fb811db..0000000 --- a/src/core/service.cpp +++ /dev/null @@ -1,128 +0,0 @@ -#include -#include - -namespace extra2d { - -SvcMgr& SvcMgr::inst() { - static SvcMgr instance; - return instance; -} - -void SvcMgr::reg(IService* svc) { - if (!svc) return; - - std::lock_guard lock(svcMapMutex_); - svcMap_[svc->name()] = Ptr(svc); - sortSvcs(); -} - -void SvcMgr::unreg(const char* name) { - std::lock_guard lock(svcMapMutex_); - svcMap_.erase(name); - sortSvcs(); -} - -Ptr SvcMgr::get(const char* name) { - std::lock_guard lock(svcMapMutex_); - auto it = svcMap_.find(name); - if (it != svcMap_.end()) { - return it->second; - } - return nullptr; -} - -bool SvcMgr::initAll() { - std::lock_guard lock(svcMapMutex_); - for (auto& svc : sortedSvcs_) { - if (svc && svc->state_ == SvcState::None) { - if (!svc->init()) { - return false; - } - svc->state_ = SvcState::Inited; - } - } - return true; -} - -void SvcMgr::pauseAll() { - std::lock_guard lock(svcMapMutex_); - for (auto& svc : sortedSvcs_) { - if (svc && svc->state_ == SvcState::Running) { - svc->pause(); - svc->state_ = SvcState::Paused; - } - } -} - -void SvcMgr::resumeAll() { - std::lock_guard lock(svcMapMutex_); - for (auto& svc : sortedSvcs_) { - if (svc && svc->state_ == SvcState::Paused) { - svc->resume(); - svc->state_ = SvcState::Running; - } - } -} - -void SvcMgr::shutdownAll() { - std::lock_guard lock(svcMapMutex_); - for (auto it = sortedSvcs_.rbegin(); it != sortedSvcs_.rend(); ++it) { - if (*it && (*it)->state_ >= SvcState::Inited) { - (*it)->shutdown(); - (*it)->state_ = SvcState::Shutdown; - } - } -} - -void SvcMgr::updateAll(float dt) { - std::lock_guard lock(svcMapMutex_); - for (auto& svc : sortedSvcs_) { - if (svc && svc->state_ >= SvcState::Inited && svc->isEnabled()) { - if (svc->state_ == SvcState::Inited) { - svc->state_ = SvcState::Running; - } - svc->update(dt); - } - } -} - -void SvcMgr::lateUpdateAll(float dt) { - std::lock_guard lock(svcMapMutex_); - for (auto& svc : sortedSvcs_) { - if (svc && svc->state_ >= SvcState::Running && svc->isEnabled()) { - svc->lateUpdate(dt); - } - } -} - -void SvcMgr::fixedUpdateAll(float dt) { - std::lock_guard lock(svcMapMutex_); - for (auto& svc : sortedSvcs_) { - if (svc && svc->state_ >= SvcState::Running && svc->isEnabled()) { - svc->fixedUpdate(dt); - } - } -} - -bool SvcMgr::has(const char* name) const { - std::lock_guard lock(svcMapMutex_); - return svcMap_.find(name) != svcMap_.end(); -} - -size_t SvcMgr::count() const { - std::lock_guard lock(svcMapMutex_); - return svcMap_.size(); -} - -void SvcMgr::sortSvcs() { - sortedSvcs_.clear(); - for (auto it = svcMap_.begin(); it != svcMap_.end(); ++it) { - sortedSvcs_.push_back(it->second); - } - std::sort(sortedSvcs_.begin(), sortedSvcs_.end(), - [](const Ptr& a, const Ptr& b) { - return a->pri() < b->pri(); - }); -} - -} // namespace extra2d diff --git a/src/module/module_registry.cpp b/src/module/module_registry.cpp new file mode 100644 index 0000000..1e368c8 --- /dev/null +++ b/src/module/module_registry.cpp @@ -0,0 +1,148 @@ +#include +#include +#include + +namespace extra2d { + +ModuleRegistry::ModuleRegistry() = default; + +ModuleRegistry::~ModuleRegistry() { + if (inited_) { + shutdownAll(); + } +} + +ModuleRegistry::ModuleRegistry(ModuleRegistry&&) noexcept = default; +ModuleRegistry& ModuleRegistry::operator=(ModuleRegistry&&) noexcept = default; + +void ModuleRegistry::registerModule(IModule* module) { + if (!module) { + return; + } + + const char* name = module->name(); + if (!name || moduleMap_.find(name) != moduleMap_.end()) { + return; // 已存在或名称为空 + } + + modules_.push_back(module); + moduleMap_[name] = module; + sorted_ = false; + + // 模块注册事件(暂不发送,避免依赖 events.h) + // event::broadcast(name, module->type()); +} + +void ModuleRegistry::unregisterModule(const char* name) { + if (!name) { + return; + } + + auto it = moduleMap_.find(name); + if (it == moduleMap_.end()) { + return; + } + + IModule* module = it->second; + + // 如果已初始化,先关闭 + if (inited_ && module) { + module->shutdown(); + } + + // 从列表中移除 + modules_.erase( + std::remove(modules_.begin(), modules_.end(), module), + modules_.end() + ); + + moduleMap_.erase(it); +} + +IModule* ModuleRegistry::getModule(const char* name) const { + if (!name) { + return nullptr; + } + + auto it = moduleMap_.find(name); + return it != moduleMap_.end() ? it->second : nullptr; +} + +bool ModuleRegistry::hasModule(const char* name) const { + if (!name) { + return false; + } + return moduleMap_.find(name) != moduleMap_.end(); +} + +std::vector ModuleRegistry::getAllModules() const { + return modules_; +} + +std::vector ModuleRegistry::getModulesByType(ModuleType type) const { + std::vector result; + for (auto* module : modules_) { + if (module && module->type() == type) { + result.push_back(module); + } + } + return result; +} + +bool ModuleRegistry::initAll() { + if (inited_) { + return true; + } + + // 按优先级排序 + sortModules(); + + // 初始化所有模块 + for (auto* module : modules_) { + if (module && !module->init()) { + // 初始化失败,关闭已初始化的模块 + shutdownAll(); + return false; + } + } + + inited_ = true; + return true; +} + +void ModuleRegistry::shutdownAll() { + if (!inited_) { + return; + } + + // 按优先级逆序关闭 + for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) { + if (*it) { + (*it)->shutdown(); + } + } + + inited_ = false; +} + +size_t ModuleRegistry::getModuleCount() const { + return modules_.size(); +} + +void ModuleRegistry::sortModules() { + if (sorted_) { + return; + } + + // 按优先级排序(数值小的优先) + std::sort(modules_.begin(), modules_.end(), + [](IModule* a, IModule* b) { + if (!a || !b) return a < b; + return a->priority() < b->priority(); + } + ); + + sorted_ = true; +} + +} // namespace extra2d diff --git a/src/module/timer_module.cpp b/src/module/timer_module.cpp new file mode 100644 index 0000000..9dd67c7 --- /dev/null +++ b/src/module/timer_module.cpp @@ -0,0 +1,221 @@ +#include +#include + +namespace extra2d { + +TimerModule::TimerModule() = default; + +TimerModule::~TimerModule() { + shutdown(); +} + +TimerModule::TimerModule(TimerModule&& other) noexcept + : timers_(std::move(other.timers_)) + , pendingRemove_(std::move(other.pendingRemove_)) + , nextId_(other.nextId_) + , timeScale_(other.timeScale_) + , inUpdate_(other.inUpdate_) { +} + +TimerModule& TimerModule::operator=(TimerModule&& other) noexcept { + if (this != &other) { + shutdown(); + + timers_ = std::move(other.timers_); + pendingRemove_ = std::move(other.pendingRemove_); + nextId_ = other.nextId_; + timeScale_ = other.timeScale_; + inUpdate_ = other.inUpdate_; + } + return *this; +} + +bool TimerModule::init() { + // 初始化完成,等待 OnUpdate 事件 + return true; +} + +void TimerModule::shutdown() { + cancelAll(); +} + +TimerId TimerModule::scheduleOnce(float delay, TimerCallback callback) { + if (delay < 0.0f) delay = 0.0f; + + TimerId id = generateId(); + auto info = std::make_unique(); + info->id = id; + info->interval = delay; + info->elapsed = 0.0f; + info->repeat = 1; + info->executed = 0; + info->paused = false; + info->cancelled = false; + info->callback = std::move(callback); + + timers_[id] = std::move(info); + return id; +} + +TimerId TimerModule::scheduleRepeat(float interval, uint32 repeat, TimerCallback callback) { + if (interval < 0.0f) interval = 0.0f; + + TimerId id = generateId(); + auto info = std::make_unique(); + info->id = id; + info->interval = interval; + info->elapsed = 0.0f; + info->repeat = repeat; + info->executed = 0; + info->paused = false; + info->cancelled = false; + info->callback = std::move(callback); + + timers_[id] = std::move(info); + return id; +} + +TimerId TimerModule::scheduleUpdate(TimerUpdateCallback callback) { + TimerId id = generateId(); + auto info = std::make_unique(); + info->id = id; + info->interval = 0.0f; + info->elapsed = 0.0f; + info->repeat = 0; + info->executed = 0; + info->paused = false; + info->cancelled = false; + info->updateCallback = std::move(callback); + + timers_[id] = std::move(info); + return id; +} + +void TimerModule::cancel(TimerId id) { + if (id == INVALID_TIMER_ID) return; + + auto it = timers_.find(id); + if (it != timers_.end()) { + if (inUpdate_) { + // 如果在更新中,标记为待删除 + it->second->cancelled = true; + pendingRemove_.push_back(id); + } else { + // 直接删除 + timers_.erase(it); + } + } +} + +void TimerModule::pause(TimerId id) { + if (id == INVALID_TIMER_ID) return; + + auto it = timers_.find(id); + if (it != timers_.end()) { + it->second->paused = true; + } +} + +void TimerModule::resume(TimerId id) { + if (id == INVALID_TIMER_ID) return; + + auto it = timers_.find(id); + if (it != timers_.end()) { + it->second->paused = false; + } +} + +void TimerModule::cancelAll() { + if (inUpdate_) { + // 如果在更新中,标记所有为待删除 + for (auto& pair : timers_) { + pair.second->cancelled = true; + pendingRemove_.push_back(pair.first); + } + } else { + // 直接清空 + timers_.clear(); + } +} + +size_t TimerModule::getActiveCount() const { + size_t count = 0; + for (const auto& pair : timers_) { + if (!pair.second->cancelled) { + ++count; + } + } + return count; +} + +void TimerModule::update(float dt) { + if (timers_.empty()) return; + + // 应用时间缩放 + dt *= timeScale_; + + inUpdate_ = true; + + // 收集需要执行的定时器 + std::vector toExecute; + std::vector toRemove; + + for (auto& pair : timers_) { + TimerInfo* info = pair.second.get(); + + if (info->cancelled || info->paused) { + continue; + } + + // 更新任务(每帧执行) + if (info->updateCallback) { + toExecute.push_back(info); + continue; + } + + // 定时任务 + info->elapsed += dt; + + if (info->elapsed >= info->interval) { + toExecute.push_back(info); + + // 重置计时器 + info->elapsed = 0.0f; + info->executed++; + + // 检查是否需要移除 + if (info->repeat > 0 && info->executed >= info->repeat) { + toRemove.push_back(info->id); + } + } + } + + // 执行回调 + for (TimerInfo* info : toExecute) { + if (info->callback) { + info->callback(); + } + if (info->updateCallback) { + info->updateCallback(dt); + } + } + + inUpdate_ = false; + + // 移除已完成的定时器 + for (TimerId id : toRemove) { + timers_.erase(id); + } + + // 处理待删除列表 + for (TimerId id : pendingRemove_) { + timers_.erase(id); + } + pendingRemove_.clear(); +} + +TimerId TimerModule::generateId() { + return nextId_++; +} + +} // namespace extra2d diff --git a/src/platform/file.cpp b/src/platform/file.cpp index 9b10c4c..aa9ea22 100644 --- a/src/platform/file.cpp +++ b/src/platform/file.cpp @@ -16,94 +16,92 @@ namespace extra2d { -FileSvc& FileSvc::inst() { - static FileSvc instance; - return instance; -} +FileModule::FileModule() = default; -bool FileSvc::init() { - if (isInited()) return true; - +FileModule::~FileModule() = default; + +FileModule::FileModule(FileModule&&) noexcept = default; +FileModule& FileModule::operator=(FileModule&&) noexcept = default; + +bool FileModule::init() { writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D"); if (writableDir_.empty()) { writableDir_ = "./"; } - - state_ = SvcState::Inited; return true; } -void FileSvc::shutdown() { - state_ = SvcState::Shutdown; +void FileModule::shutdown() { + // 清理工作 } -bool FileSvc::exists(const std::string& path) const { +bool FileModule::exists(const std::string& path) const { struct stat st; return stat(path.c_str(), &st) == 0; } -bool FileSvc::isDir(const std::string& path) const { +bool FileModule::isDir(const std::string& path) const { struct stat st; if (stat(path.c_str(), &st) != 0) return false; return (st.st_mode & S_IFDIR) != 0; } -FileData FileSvc::read(const std::string& path) const { +FileData FileModule::read(const std::string& path) const { FileData result; - + std::ifstream file(path, std::ios::binary | std::ios::ate); if (!file.is_open()) { result.error = "Cannot open file: " + path; return result; } - + std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); - + result.data.resize(static_cast(size)); if (!file.read(reinterpret_cast(result.data.data()), size)) { result.error = "Failed to read file: " + path; return result; } - + result.ok = true; return result; } -std::string FileSvc::readString(const std::string& path) const { +std::string FileModule::readString(const std::string& path) const { std::ifstream file(path); if (!file.is_open()) return ""; - + std::stringstream buffer; buffer << file.rdbuf(); return buffer.str(); } -bool FileSvc::write(const std::string& path, const void* data, size_t size) const { +bool FileModule::write(const std::string& path, const void* data, size_t size) const { std::ofstream file(path, std::ios::binary); if (!file.is_open()) return false; - + file.write(static_cast(data), static_cast(size)); return file.good(); } -bool FileSvc::writeString(const std::string& path, const std::string& content) const { +bool FileModule::writeString(const std::string& path, const std::string& content) const { return write(path, content.data(), content.size()); } -bool FileSvc::append(const std::string& path, const void* data, size_t size) const { +bool FileModule::append(const std::string& path, const void* data, size_t size) const { std::ofstream file(path, std::ios::binary | std::ios::app); if (!file.is_open()) return false; - + file.write(static_cast(data), static_cast(size)); return file.good(); } -bool FileSvc::remove(const std::string& path) const { +bool FileModule::remove(const std::string& path) const { return std::remove(path.c_str()) == 0; } -bool FileSvc::mkdir(const std::string& path) const { +bool FileModule::mkdir(const std::string& path) const { #ifdef _WIN32 return mkdir_impl(path.c_str(), 0755) == 0 || errno == EEXIST; #else @@ -111,41 +109,41 @@ bool FileSvc::mkdir(const std::string& path) const { #endif } -std::vector FileSvc::listDir(const std::string& path) const { +std::vector FileModule::listDir(const std::string& path) const { std::vector result; - + #ifdef _WIN32 WIN32_FIND_DATAA findData; std::string searchPath = path + "\\*"; HANDLE hFind = FindFirstFileA(searchPath.c_str(), &findData); - + if (hFind == INVALID_HANDLE_VALUE) return result; - + do { std::string name = findData.cFileName; if (name == "." || name == "..") continue; - + FileInfo info; info.name = name; info.path = join(path, name); info.isDir = (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; if (!info.isDir) { - info.size = static_cast(findData.nFileSizeLow) | + info.size = static_cast(findData.nFileSizeLow) | (static_cast(findData.nFileSizeHigh) << 32); } result.push_back(info); } while (FindNextFileA(hFind, &findData)); - + FindClose(hFind); #else DIR* dir = opendir(path.c_str()); if (!dir) return result; - + struct dirent* entry; while ((entry = readdir(dir)) != nullptr) { std::string name = entry->d_name; if (name == "." || name == "..") continue; - + FileInfo info; info.name = name; info.path = join(path, name); @@ -155,46 +153,46 @@ std::vector FileSvc::listDir(const std::string& path) const { } result.push_back(info); } - + closedir(dir); #endif - + return result; } -int64 FileSvc::fileSize(const std::string& path) const { +int64 FileModule::fileSize(const std::string& path) const { struct stat st; if (stat(path.c_str(), &st) != 0) return -1; return static_cast(st.st_size); } -std::string FileSvc::ext(const std::string& path) const { +std::string FileModule::ext(const std::string& path) const { size_t pos = path.find_last_of('.'); if (pos == std::string::npos || pos == 0) return ""; - + size_t lastSep = path.find_last_of("/\\"); if (lastSep != std::string::npos && pos < lastSep) return ""; - + return path.substr(pos + 1); } -std::string FileSvc::fileName(const std::string& path) const { +std::string FileModule::fileName(const std::string& path) const { size_t pos = path.find_last_of("/\\"); if (pos == std::string::npos) return path; return path.substr(pos + 1); } -std::string FileSvc::dirName(const std::string& path) const { +std::string FileModule::dirName(const std::string& path) const { size_t pos = path.find_last_of("/\\"); if (pos == std::string::npos) return "."; if (pos == 0) return "/"; return path.substr(0, pos); } -std::string FileSvc::join(const std::string& a, const std::string& b) const { +std::string FileModule::join(const std::string& a, const std::string& b) const { if (a.empty()) return b; if (b.empty()) return a; - + char last = a.back(); if (last == '/' || last == '\\') { return a + b; @@ -202,11 +200,11 @@ std::string FileSvc::join(const std::string& a, const std::string& b) const { return a + "/" + b; } -std::string FileSvc::writableDir() const { +std::string FileModule::writableDir() const { return writableDir_; } -std::string FileSvc::assetPath(const std::string& relPath) const { +std::string FileModule::assetPath(const std::string& relPath) const { if (assetRoot_.empty()) return relPath; return join(assetRoot_, relPath); } diff --git a/src/platform/input.cpp b/src/platform/input.cpp index 1520930..8171310 100644 --- a/src/platform/input.cpp +++ b/src/platform/input.cpp @@ -1,76 +1,131 @@ #include -#include +#include #include #include #include namespace extra2d { -InputSvc& InputSvc::inst() { - static InputSvc instance; - return instance; +InputModule::InputModule() = default; + +InputModule::~InputModule() { + shutdown(); } -bool InputSvc::init() { - if (isInited()) return true; - +InputModule::InputModule(InputModule&& other) noexcept + : mouseX_(other.mouseX_) + , mouseY_(other.mouseY_) + , mouseWheel_(other.mouseWheel_) + , activeTouches_(std::move(other.activeTouches_)) + , endedTouches_(std::move(other.endedTouches_)) + , onKeyDown_(std::move(other.onKeyDown_)) + , onKeyUp_(std::move(other.onKeyUp_)) + , onMouseDown_(std::move(other.onMouseDown_)) + , onMouseUp_(std::move(other.onMouseUp_)) + , onTouchBegan_(std::move(other.onTouchBegan_)) + , onTouchMoved_(std::move(other.onTouchMoved_)) + , onTouchEnded_(std::move(other.onTouchEnded_)) { + std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT); + std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT); + std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast(MouseBtn::Count)); + std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast(MouseBtn::Count)); + std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_)); + std::memcpy(padState_, other.padState_, sizeof(padState_)); + std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_)); + + for (int32 i = 0; i < MAX_GAMEPADS; ++i) { + other.gamepads_[i] = nullptr; + } +} + +InputModule& InputModule::operator=(InputModule&& other) noexcept { + if (this != &other) { + shutdown(); + + mouseX_ = other.mouseX_; + mouseY_ = other.mouseY_; + mouseWheel_ = other.mouseWheel_; + activeTouches_ = std::move(other.activeTouches_); + endedTouches_ = std::move(other.endedTouches_); + onKeyDown_ = std::move(other.onKeyDown_); + onKeyUp_ = std::move(other.onKeyUp_); + onMouseDown_ = std::move(other.onMouseDown_); + onMouseUp_ = std::move(other.onMouseUp_); + onTouchBegan_ = std::move(other.onTouchBegan_); + onTouchMoved_ = std::move(other.onTouchMoved_); + onTouchEnded_ = std::move(other.onTouchEnded_); + + std::memcpy(keyState_.data(), other.keyState_.data(), KEY_COUNT); + std::memcpy(keyPrev_.data(), other.keyPrev_.data(), KEY_COUNT); + std::memcpy(mouseState_.data(), other.mouseState_.data(), static_cast(MouseBtn::Count)); + std::memcpy(mousePrev_.data(), other.mousePrev_.data(), static_cast(MouseBtn::Count)); + std::memcpy(gamepads_, other.gamepads_, sizeof(gamepads_)); + std::memcpy(padState_, other.padState_, sizeof(padState_)); + std::memcpy(padPrev_, other.padPrev_, sizeof(padPrev_)); + + for (int32 i = 0; i < MAX_GAMEPADS; ++i) { + other.gamepads_[i] = nullptr; + } + } + return *this; +} + +bool InputModule::init() { std::memset(keyState_.data(), 0, KEY_COUNT); std::memset(keyPrev_.data(), 0, KEY_COUNT); std::memset(mouseState_.data(), 0, static_cast(MouseBtn::Count)); std::memset(mousePrev_.data(), 0, static_cast(MouseBtn::Count)); - + for (int32 i = 0; i < MAX_GAMEPADS; ++i) { gamepads_[i] = nullptr; std::memset(padState_[i].data(), 0, static_cast(GamepadBtn::Count)); std::memset(padPrev_[i].data(), 0, static_cast(GamepadBtn::Count)); } - + activeTouches_.clear(); activeTouches_.reserve(MAX_TOUCHES); endedTouches_.clear(); - + int32 numJoysticks = SDL_NumJoysticks(); for (int32 i = 0; i < numJoysticks && i < MAX_GAMEPADS; ++i) { if (SDL_IsGameController(i)) { openGamepad(i); } } - - state_ = SvcState::Inited; + return true; } -void InputSvc::shutdown() { +void InputModule::shutdown() { for (int32 i = 0; i < MAX_GAMEPADS; ++i) { closeGamepad(i); } activeTouches_.clear(); endedTouches_.clear(); - state_ = SvcState::Shutdown; } -void InputSvc::update(float dt) { +void InputModule::update() { std::memcpy(keyPrev_.data(), keyState_.data(), KEY_COUNT); std::memcpy(mousePrev_.data(), mouseState_.data(), static_cast(MouseBtn::Count)); - + for (int32 i = 0; i < MAX_GAMEPADS; ++i) { if (gamepads_[i]) { std::memcpy(padPrev_[i].data(), padState_[i].data(), static_cast(GamepadBtn::Count)); } } - + mouseWheel_ = 0; - + const uint8* state = SDL_GetKeyboardState(nullptr); std::memcpy(keyState_.data(), state, KEY_COUNT); - + uint32 btnState = SDL_GetMouseState(&mouseX_, &mouseY_); mouseState_[static_cast(MouseBtn::Left)] = (btnState & SDL_BUTTON_LMASK) ? 1 : 0; mouseState_[static_cast(MouseBtn::Middle)] = (btnState & SDL_BUTTON_MMASK) ? 1 : 0; mouseState_[static_cast(MouseBtn::Right)] = (btnState & SDL_BUTTON_RMASK) ? 1 : 0; mouseState_[static_cast(MouseBtn::X1)] = (btnState & SDL_BUTTON_X1MASK) ? 1 : 0; mouseState_[static_cast(MouseBtn::X2)] = (btnState & SDL_BUTTON_X2MASK) ? 1 : 0; - + for (int32 i = 0; i < MAX_GAMEPADS; ++i) { if (gamepads_[i]) { SDL_GameController* gc = gamepads_[i]; @@ -91,22 +146,22 @@ void InputSvc::update(float dt) { padState_[i][static_cast(GamepadBtn::DPadRight)] = SDL_GameControllerGetButton(gc, SDL_CONTROLLER_BUTTON_DPAD_RIGHT); } } - + endedTouches_.clear(); - + for (auto& touch : activeTouches_) { if (touch.state == TouchState::Ended || touch.state == TouchState::Cancelled) { endedTouches_.push_back(touch); } } - + activeTouches_.erase( std::remove_if(activeTouches_.begin(), activeTouches_.end(), [](const TouchPoint& t) { return t.state == TouchState::Ended || t.state == TouchState::Cancelled; }), activeTouches_.end()); - + for (auto& touch : activeTouches_) { touch.prevX = touch.x; touch.prevY = touch.y; @@ -118,38 +173,65 @@ void InputSvc::update(float dt) { } } -void InputSvc::processEvent(const SDL_Event& evt) { +void InputModule::processEvent(const SDL_Event& evt) { switch (evt.type) { case SDL_KEYDOWN: - if (evt.key.repeat == 0 && onKeyDown_) { - onKeyDown_(static_cast(evt.key.keysym.scancode)); + if (evt.key.repeat == 0) { + Key key = static_cast(evt.key.keysym.scancode); + // 发送按键按下事件 + events::OnKeyDown::emit(static_cast(key)); + if (onKeyDown_) { + onKeyDown_(key); + } } break; - case SDL_KEYUP: + case SDL_KEYUP: { + Key key = static_cast(evt.key.keysym.scancode); + // 发送按键释放事件 + events::OnKeyUp::emit(static_cast(key)); if (onKeyUp_) { - onKeyUp_(static_cast(evt.key.keysym.scancode)); + onKeyUp_(key); } break; - case SDL_MOUSEBUTTONDOWN: + } + case SDL_MOUSEBUTTONDOWN: { + MouseBtn btn = static_cast(evt.button.button - 1); + // 发送鼠标按下事件 + events::OnMouseDown::emit(static_cast(btn), evt.button.x, evt.button.y); if (onMouseDown_) { - onMouseDown_(static_cast(evt.button.button - 1), evt.button.x, evt.button.y); + onMouseDown_(btn, evt.button.x, evt.button.y); } break; - case SDL_MOUSEBUTTONUP: + } + case SDL_MOUSEBUTTONUP: { + MouseBtn btn = static_cast(evt.button.button - 1); + // 发送鼠标释放事件 + events::OnMouseUp::emit(static_cast(btn), evt.button.x, evt.button.y); if (onMouseUp_) { - onMouseUp_(static_cast(evt.button.button - 1), evt.button.x, evt.button.y); + onMouseUp_(btn, evt.button.x, evt.button.y); } break; + } + case SDL_MOUSEMOTION: + // 发送鼠标移动事件 + events::OnMouseMove::emit(evt.motion.x, evt.motion.y); + break; case SDL_MOUSEWHEEL: mouseWheel_ = evt.wheel.y; + // 发送鼠标滚轮事件 + events::OnMouseWheel::emit(evt.wheel.y); break; case SDL_CONTROLLERDEVICEADDED: openGamepad(evt.cdevice.which); + // 发送手柄连接事件 + events::OnGamepadConnect::emit(evt.cdevice.which); break; case SDL_CONTROLLERDEVICEREMOVED: for (int32 i = 0; i < MAX_GAMEPADS; ++i) { if (gamepads_[i] && SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamepads_[i])) == evt.cdevice.which) { closeGamepad(i); + // 发送手柄断开事件 + events::OnGamepadDisconnect::emit(i); break; } } @@ -166,12 +248,12 @@ void InputSvc::processEvent(const SDL_Event& evt) { } } -void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) { +void InputModule::processTouchDown(const SDL_TouchFingerEvent& evt) { if (static_cast(activeTouches_.size()) >= MAX_TOUCHES) return; - + int32 winW = 0, winH = 0; SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH); - + TouchPoint touch; touch.id = static_cast(evt.fingerId); touch.x = evt.x * static_cast(winW); @@ -182,29 +264,35 @@ void InputSvc::processTouchDown(const SDL_TouchFingerEvent& evt) { touch.deltaY = 0.0f; touch.state = TouchState::Began; touch.pressure = evt.pressure; - + activeTouches_.push_back(touch); - + + // 发送触摸开始事件 + events::OnTouchBegin::emit(static_cast(touch.id), static_cast(touch.x), static_cast(touch.y)); + if (onTouchBegan_) { onTouchBegan_(touch); } } -void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) { +void InputModule::processTouchUp(const SDL_TouchFingerEvent& evt) { int64 fingerId = static_cast(evt.fingerId); - + for (auto& touch : activeTouches_) { if (touch.id == fingerId) { int32 winW = 0, winH = 0; SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH); - + touch.x = evt.x * static_cast(winW); touch.y = evt.y * static_cast(winH); touch.deltaX = touch.x - touch.prevX; touch.deltaY = touch.y - touch.prevY; touch.state = TouchState::Ended; touch.pressure = evt.pressure; - + + // 发送触摸结束事件 + events::OnTouchEnd::emit(static_cast(touch.id), static_cast(touch.x), static_cast(touch.y)); + if (onTouchEnded_) { onTouchEnded_(touch); } @@ -213,21 +301,24 @@ void InputSvc::processTouchUp(const SDL_TouchFingerEvent& evt) { } } -void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) { +void InputModule::processTouchMotion(const SDL_TouchFingerEvent& evt) { int64 fingerId = static_cast(evt.fingerId); - + for (auto& touch : activeTouches_) { if (touch.id == fingerId) { int32 winW = 0, winH = 0; SDL_GetWindowSize(SDL_GetWindowFromID(evt.windowID), &winW, &winH); - + touch.x = evt.x * static_cast(winW); touch.y = evt.y * static_cast(winH); touch.deltaX = touch.x - touch.prevX; touch.deltaY = touch.y - touch.prevY; touch.state = TouchState::Moved; touch.pressure = evt.pressure; - + + // 发送触摸移动事件 + events::OnTouchMove::emit(static_cast(touch.id), static_cast(touch.x), static_cast(touch.y)); + if (onTouchMoved_) { onTouchMoved_(touch); } @@ -236,10 +327,10 @@ void InputSvc::processTouchMotion(const SDL_TouchFingerEvent& evt) { } } -void InputSvc::openGamepad(int32 idx) { +void InputModule::openGamepad(int32 idx) { if (idx < 0 || idx >= MAX_GAMEPADS) return; if (gamepads_[idx]) return; - + SDL_GameController* gc = SDL_GameControllerOpen(idx); if (gc) { gamepads_[idx] = gc; @@ -248,7 +339,7 @@ void InputSvc::openGamepad(int32 idx) { } } -void InputSvc::closeGamepad(int32 idx) { +void InputModule::closeGamepad(int32 idx) { if (idx < 0 || idx >= MAX_GAMEPADS) return; if (gamepads_[idx]) { SDL_GameControllerClose(gamepads_[idx]); @@ -256,70 +347,70 @@ void InputSvc::closeGamepad(int32 idx) { } } -bool InputSvc::isKeyDown(Key key) const { +bool InputModule::isKeyDown(Key key) const { if (key < 0 || key >= KEY_COUNT) return false; return keyState_[key] != 0; } -bool InputSvc::isKeyPressed(Key key) const { +bool InputModule::isKeyPressed(Key key) const { if (key < 0 || key >= KEY_COUNT) return false; return keyState_[key] != 0 && keyPrev_[key] == 0; } -bool InputSvc::isKeyReleased(Key key) const { +bool InputModule::isKeyReleased(Key key) const { if (key < 0 || key >= KEY_COUNT) return false; return keyState_[key] == 0 && keyPrev_[key] != 0; } -void InputSvc::getMousePos(int32& x, int32& y) const { +void InputModule::getMousePos(int32& x, int32& y) const { x = mouseX_; y = mouseY_; } -void InputSvc::getMousePos(float& x, float& y) const { +void InputModule::getMousePos(float& x, float& y) const { x = static_cast(mouseX_); y = static_cast(mouseY_); } -bool InputSvc::isMouseBtnDown(MouseBtn btn) const { +bool InputModule::isMouseBtnDown(MouseBtn btn) const { size_t idx = static_cast(btn); if (idx >= static_cast(MouseBtn::Count)) return false; return mouseState_[idx] != 0; } -bool InputSvc::isMouseBtnPressed(MouseBtn btn) const { +bool InputModule::isMouseBtnPressed(MouseBtn btn) const { size_t idx = static_cast(btn); if (idx >= static_cast(MouseBtn::Count)) return false; return mouseState_[idx] != 0 && mousePrev_[idx] == 0; } -bool InputSvc::isMouseBtnReleased(MouseBtn btn) const { +bool InputModule::isMouseBtnReleased(MouseBtn btn) const { size_t idx = static_cast(btn); if (idx >= static_cast(MouseBtn::Count)) return false; return mouseState_[idx] == 0 && mousePrev_[idx] != 0; } -int32 InputSvc::getMouseWheel() const { +int32 InputModule::getMouseWheel() const { return mouseWheel_; } -int32 InputSvc::touchCount() const { +int32 InputModule::touchCount() const { return static_cast(activeTouches_.size()); } -const TouchPoint* InputSvc::getTouch(int32 idx) const { +const TouchPoint* InputModule::getTouch(int32 idx) const { if (idx < 0 || idx >= static_cast(activeTouches_.size())) return nullptr; return &activeTouches_[idx]; } -const TouchPoint* InputSvc::getTouchById(int64 id) const { +const TouchPoint* InputModule::getTouchById(int64 id) const { for (const auto& touch : activeTouches_) { if (touch.id == id) return &touch; } return nullptr; } -int32 InputSvc::gamepadCount() const { +int32 InputModule::gamepadCount() const { int32 count = 0; for (int32 i = 0; i < MAX_GAMEPADS; ++i) { if (gamepads_[i]) ++count; @@ -327,7 +418,7 @@ int32 InputSvc::gamepadCount() const { return count; } -bool InputSvc::isGamepadBtnDown(int32 idx, GamepadBtn btn) const { +bool InputModule::isGamepadBtnDown(int32 idx, GamepadBtn btn) const { if (idx < 0 || idx >= MAX_GAMEPADS) return false; if (!gamepads_[idx]) return false; size_t b = static_cast(btn); @@ -335,7 +426,7 @@ bool InputSvc::isGamepadBtnDown(int32 idx, GamepadBtn btn) const { return padState_[idx][b] != 0; } -bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const { +bool InputModule::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const { if (idx < 0 || idx >= MAX_GAMEPADS) return false; if (!gamepads_[idx]) return false; size_t b = static_cast(btn); @@ -343,10 +434,10 @@ bool InputSvc::isGamepadBtnPressed(int32 idx, GamepadBtn btn) const { return padState_[idx][b] != 0 && padPrev_[idx][b] == 0; } -float InputSvc::getGamepadAxis(int32 idx, GamepadAxis axis) const { +float InputModule::getGamepadAxis(int32 idx, GamepadAxis axis) const { if (idx < 0 || idx >= MAX_GAMEPADS) return 0.0f; if (!gamepads_[idx]) return 0.0f; - + SDL_GameControllerAxis sdlAxis = SDL_CONTROLLER_AXIS_INVALID; switch (axis) { case GamepadAxis::LeftX: sdlAxis = SDL_CONTROLLER_AXIS_LEFTX; break; @@ -357,7 +448,7 @@ float InputSvc::getGamepadAxis(int32 idx, GamepadAxis axis) const { case GamepadAxis::TriggerRight: sdlAxis = SDL_CONTROLLER_AXIS_TRIGGERRIGHT; break; default: return 0.0f; } - + int16 value = SDL_GameControllerGetAxis(gamepads_[idx], sdlAxis); return static_cast(value) / 32767.0f; } diff --git a/src/platform/window.cpp b/src/platform/window.cpp index 126ea50..9b08a53 100644 --- a/src/platform/window.cpp +++ b/src/platform/window.cpp @@ -1,178 +1,241 @@ -#include -#include -#include #include +#include +#include +#include namespace extra2d { -WindowSvc &WindowSvc::inst() { - static WindowSvc instance; - return instance; +WindowModule::WindowModule() = default; + +WindowModule::~WindowModule() { + shutdown(); } -bool WindowSvc::init() { - if (isInited()) +WindowModule::WindowModule(WindowModule&& other) noexcept + : window_(other.window_) + , glCtx_(other.glCtx_) + , shouldClose_(other.shouldClose_) + , vsync_(other.vsync_) + , onClose_(std::move(other.onClose_)) + , onResize_(std::move(other.onResize_)) { + other.window_ = nullptr; + other.glCtx_ = nullptr; +} + +WindowModule& WindowModule::operator=(WindowModule&& other) noexcept { + if (this != &other) { + shutdown(); + window_ = other.window_; + glCtx_ = other.glCtx_; + shouldClose_ = other.shouldClose_; + vsync_ = other.vsync_; + onClose_ = std::move(other.onClose_); + onResize_ = std::move(other.onResize_); + other.window_ = nullptr; + other.glCtx_ = nullptr; + } + return *this; +} + +bool WindowModule::init() { + if (!Sdl2::initVideo()) { + return false; + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); + SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); + return true; - - if (!Sdl2::initVideo()) { - return false; - } - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); - SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); - SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); - SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - - state_ = SvcState::Inited; - return true; } -void WindowSvc::shutdown() { - if (glCtx_) { - SDL_GL_DeleteContext(glCtx_); - glCtx_ = nullptr; - } - if (window_) { - SDL_DestroyWindow(window_); - window_ = nullptr; - } - state_ = SvcState::Shutdown; +void WindowModule::shutdown() { + if (glCtx_) { + SDL_GL_DeleteContext(glCtx_); + glCtx_ = nullptr; + } + if (window_) { + SDL_DestroyWindow(window_); + window_ = nullptr; + } } -bool WindowSvc::create(const WindowCfg &cfg) { - if (window_) +bool WindowModule::create(const WindowCfg& cfg) { + if (window_) + return true; + + uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; + if (cfg.resizable) { + flags |= SDL_WINDOW_RESIZABLE; + } + if (cfg.fullscreen) { + flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + } + + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor); + + window_ = SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags); + + if (!window_) { + E2D_LOG_ERROR("Failed to create window: {}", SDL_GetError()); + return false; + } + + glCtx_ = SDL_GL_CreateContext(window_); + if (!glCtx_) { + E2D_LOG_ERROR("Failed to create OpenGL context: {}", SDL_GetError()); + SDL_DestroyWindow(window_); + window_ = nullptr; + return false; + } + + setVsync(cfg.vsync); + vsync_ = cfg.vsync; + + // 发送窗口显示事件 + events::OnShow::emit(); + return true; - - uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN; - if (cfg.resizable) { - flags |= SDL_WINDOW_RESIZABLE; - } - if (cfg.fullscreen) { - flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - } - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, cfg.glMajor); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, cfg.glMinor); - - window_ = - SDL_CreateWindow(cfg.title.c_str(), SDL_WINDOWPOS_CENTERED, - SDL_WINDOWPOS_CENTERED, cfg.width, cfg.height, flags); - - if (!window_) { - return false; - } - - glCtx_ = SDL_GL_CreateContext(window_); - if (!glCtx_) { - SDL_DestroyWindow(window_); - window_ = nullptr; - return false; - } - - setVsync(cfg.vsync); - vsync_ = cfg.vsync; - - return true; } -bool WindowSvc::pollEvents() { - SDL_Event evt; - while (SDL_PollEvent(&evt)) { - switch (evt.type) { - case SDL_QUIT: - shouldClose_ = true; - if (onClose_) - onClose_(); - break; - case SDL_WINDOWEVENT: - if (evt.window.event == SDL_WINDOWEVENT_RESIZED) { - if (onResize_) { - onResize_(evt.window.data1, evt.window.data2); +bool WindowModule::pollEvents() { + SDL_Event evt; + while (SDL_PollEvent(&evt)) { + switch (evt.type) { + case SDL_QUIT: + shouldClose_ = true; + // 发送窗口关闭事件 + events::OnClose::emit(); + if (onClose_) + onClose_(); + break; + case SDL_WINDOWEVENT: + handleWindowEvent(evt.window); + break; + default: + // 输入事件由 InputModule 处理 + break; } - } - break; - default: - if (INPUT_SVC.isInited()) { - INPUT_SVC.processEvent(evt); - } - break; } - } - return !shouldClose_; + return !shouldClose_; } -void WindowSvc::swapBuffers() { - if (window_ && glCtx_) { - SDL_GL_SwapWindow(window_); - } -} - -Size WindowSvc::getSize() const { - if (!window_) - return Size(0, 0); - int w, h; - SDL_GetWindowSize(window_, &w, &h); - return Size(w, h); -} - -Vec2 WindowSvc::getPosition() const { - if (!window_) - return Vec2(0, 0); - int x, y; - SDL_GetWindowPosition(window_, &x, &y); - return Vec2(static_cast(x), static_cast(y)); -} - -void WindowSvc::setSize(int32 w, int32 h) { - if (window_) { - SDL_SetWindowSize(window_, w, h); - } -} - -void WindowSvc::setTitle(const std::string &title) { - if (window_) { - SDL_SetWindowTitle(window_, title.c_str()); - } -} - -void WindowSvc::setFullscreen(bool fullscreen) { - if (window_) { - SDL_SetWindowFullscreen(window_, - fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); - } -} - -bool WindowSvc::isFullscreen() const { - if (!window_) - return false; - uint32 flags = SDL_GetWindowFlags(window_); - return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; -} - -void WindowSvc::setVsync(bool vsync) { - SDL_GL_SetSwapInterval(vsync ? 1 : 0); - vsync_ = vsync; -} - -bool WindowSvc::isVsync() const { return vsync_; } - -void WindowSvc::setVisible(bool visible) { - if (window_) { - if (visible) { - SDL_ShowWindow(window_); - } else { - SDL_HideWindow(window_); +void WindowModule::handleWindowEvent(const SDL_WindowEvent& evt) { + switch (evt.event) { + case SDL_WINDOWEVENT_RESIZED: + // 发送窗口大小改变事件 + events::OnResize::emit(evt.data1, evt.data2); + if (onResize_) { + onResize_(evt.data1, evt.data2); + } + break; + case SDL_WINDOWEVENT_FOCUS_GAINED: + // 发送窗口获得焦点事件 + events::OnFocus::emit(); + break; + case SDL_WINDOWEVENT_FOCUS_LOST: + // 发送窗口失去焦点事件 + events::OnBlur::emit(); + break; + case SDL_WINDOWEVENT_SHOWN: + // 发送窗口显示事件 + events::OnShow::emit(); + break; + case SDL_WINDOWEVENT_HIDDEN: + // 发送窗口隐藏事件 + events::OnHide::emit(); + break; + case SDL_WINDOWEVENT_CLOSE: + shouldClose_ = true; + // 发送窗口关闭事件 + events::OnClose::emit(); + if (onClose_) + onClose_(); + break; } - } } -bool WindowSvc::isVisible() const { - if (!window_) - return false; - uint32 flags = SDL_GetWindowFlags(window_); - return (flags & SDL_WINDOW_SHOWN) != 0; +void WindowModule::swapBuffers() { + if (window_ && glCtx_) { + SDL_GL_SwapWindow(window_); + } +} + +Size WindowModule::getSize() const { + if (!window_) + return Size(0, 0); + int w, h; + SDL_GetWindowSize(window_, &w, &h); + return Size(w, h); +} + +Vec2 WindowModule::getPosition() const { + if (!window_) + return Vec2(0, 0); + int x, y; + SDL_GetWindowPosition(window_, &x, &y); + return Vec2(static_cast(x), static_cast(y)); +} + +void WindowModule::setSize(int32 w, int32 h) { + if (window_) { + SDL_SetWindowSize(window_, w, h); + } +} + +void WindowModule::setTitle(const std::string& title) { + if (window_) { + SDL_SetWindowTitle(window_, title.c_str()); + } +} + +void WindowModule::setFullscreen(bool fullscreen) { + if (window_) { + SDL_SetWindowFullscreen(window_, + fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); + } +} + +bool WindowModule::isFullscreen() const { + if (!window_) + return false; + uint32 flags = SDL_GetWindowFlags(window_); + return (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; +} + +void WindowModule::setVsync(bool vsync) { + SDL_GL_SetSwapInterval(vsync ? 1 : 0); + vsync_ = vsync; +} + +bool WindowModule::isVsync() const { + return vsync_; +} + +void WindowModule::setVisible(bool visible) { + if (window_) { + if (visible) { + SDL_ShowWindow(window_); + // 发送窗口显示事件 + events::OnShow::emit(); + } else { + SDL_HideWindow(window_); + // 发送窗口隐藏事件 + events::OnHide::emit(); + } + } +} + +bool WindowModule::isVisible() const { + if (!window_) + return false; + uint32 flags = SDL_GetWindowFlags(window_); + return (flags & SDL_WINDOW_SHOWN) != 0; } } // namespace extra2d diff --git a/src/plugin/plugin_loader.cpp b/src/plugin/plugin_loader.cpp new file mode 100644 index 0000000..c219cb1 --- /dev/null +++ b/src/plugin/plugin_loader.cpp @@ -0,0 +1,337 @@ +#include +#include +#include +#include + +#ifdef _WIN32 + #include +#else + #include +#endif + +namespace extra2d { + +PluginLoader::PluginLoader() = default; + +PluginLoader::~PluginLoader() { + if (inited_) { + shutdownAll(); + } + + // 卸载所有动态库插件 + for (auto& pair : plugins_) { + if (pair.second.isDynamic && pair.second.handle) { + if (pair.second.plugin) { + pair.second.plugin->unload(); + if (pair.second.owned) { + delete pair.second.plugin; + } + } + unloadDynamicLibrary(pair.second.handle); + } + } +} + +PluginLoader::PluginLoader(PluginLoader&&) noexcept = default; +PluginLoader& PluginLoader::operator=(PluginLoader&&) noexcept = default; + +bool PluginLoader::loadFromLibrary(const char* path) { + if (!path) { + return false; + } + + // 加载动态库 + void* handle = loadDynamicLibrary(path); + if (!handle) { + return false; + } + + // 获取创建函数 + using CreateFunc = IPlugin* (*)(); + CreateFunc createFunc = reinterpret_cast( + getSymbol(handle, "extra2d_create_plugin") + ); + + if (!createFunc) { + unloadDynamicLibrary(handle); + return false; + } + + // 创建插件实例 + IPlugin* plugin = createFunc(); + if (!plugin) { + unloadDynamicLibrary(handle); + return false; + } + + // 检查是否已存在 + const char* name = plugin->getInfo().name.c_str(); + if (hasPlugin(name)) { + delete plugin; + unloadDynamicLibrary(handle); + return false; + } + + // 添加到插件列表 + PluginEntry entry; + entry.plugin = plugin; + entry.handle = handle; + entry.isDynamic = true; + entry.owned = true; + + plugins_[name] = entry; + sortedPlugins_.clear(); + + // 插件加载事件(暂不发送,避免依赖 events.h) + // event::broadcast(name, plugin->getInfo().version.c_str()); + + // 如果已经初始化,立即加载插件 + if (inited_) { + if (!plugin->load()) { + unloadPlugin(name); + return false; + } + } + + return true; +} + +void PluginLoader::registerPlugin(IPlugin* plugin) { + if (!plugin) { + return; + } + + const char* name = plugin->getInfo().name.c_str(); + if (!name || hasPlugin(name)) { + return; + } + + PluginEntry entry; + entry.plugin = plugin; + entry.handle = nullptr; + entry.isDynamic = false; + entry.owned = false; + + plugins_[name] = entry; + sortedPlugins_.clear(); + + // 插件加载事件(暂不发送,避免依赖 events.h) + // event::broadcast(name, plugin->getInfo().version.c_str()); + + // 如果已经初始化,立即加载插件 + if (inited_) { + plugin->load(); + } +} + +void PluginLoader::unloadPlugin(const char* name) { + if (!name) { + return; + } + + auto it = plugins_.find(name); + if (it == plugins_.end()) { + return; + } + + PluginEntry& entry = it->second; + + // 如果已初始化,先卸载 + if (inited_ && entry.plugin) { + entry.plugin->unload(); + } + + // 如果是动态加载且由加载器管理,删除实例 + if (entry.isDynamic && entry.owned && entry.plugin) { + delete entry.plugin; + } + + // 卸载动态库 + if (entry.handle) { + unloadDynamicLibrary(entry.handle); + } + + plugins_.erase(it); + sortedPlugins_.clear(); +} + +IPlugin* PluginLoader::getPlugin(const char* name) const { + if (!name) { + return nullptr; + } + + auto it = plugins_.find(name); + return it != plugins_.end() ? it->second.plugin : nullptr; +} + +bool PluginLoader::hasPlugin(const char* name) const { + if (!name) { + return false; + } + return plugins_.find(name) != plugins_.end(); +} + +std::vector PluginLoader::getAllPlugins() const { + std::vector result; + for (const auto& pair : plugins_) { + if (pair.second.plugin) { + result.push_back(pair.second.plugin); + } + } + return result; +} + +bool PluginLoader::initAll() { + if (inited_) { + return true; + } + + // 按依赖顺序排序 + sortPluginsByDependencies(); + + // 加载所有插件 + for (auto* plugin : sortedPlugins_) { + if (plugin && !plugin->load()) { + // 加载失败,卸载已加载的插件 + shutdownAll(); + return false; + } + } + + inited_ = true; + return true; +} + +void PluginLoader::shutdownAll() { + if (!inited_) { + return; + } + + // 按依赖逆序卸载 + for (auto it = sortedPlugins_.rbegin(); it != sortedPlugins_.rend(); ++it) { + if (*it) { + (*it)->unload(); + } + } + + inited_ = false; +} + +size_t PluginLoader::getPluginCount() const { + return plugins_.size(); +} + +void PluginLoader::addSearchPath(const char* path) { + if (path) { + searchPaths_.push_back(path); + } +} + +size_t PluginLoader::loadPluginsFromDirectory(const char* directory) { + // TODO: 实现目录扫描加载 + // 需要平台相关的文件系统操作 + return 0; +} + +bool PluginLoader::resolveDependencies(IPlugin* plugin) { + if (!plugin) { + return false; + } + + return checkDependencies(plugin->getDependencies()); +} + +bool PluginLoader::checkDependencies(const std::vector& dependencies) { + for (const auto& dep : dependencies) { + if (!hasPlugin(dep.c_str())) { + return false; + } + } + return true; +} + +void PluginLoader::sortPluginsByDependencies() { + if (!sortedPlugins_.empty()) { + return; + } + + // 拓扑排序处理依赖关系 + std::unordered_map inDegree; + std::unordered_map> graph; + + // 初始化 + for (const auto& pair : plugins_) { + inDegree[pair.first] = 0; + } + + // 构建图 + for (const auto& pair : plugins_) { + if (!pair.second.plugin) continue; + + for (const auto& dep : pair.second.plugin->getDependencies()) { + graph[dep].push_back(pair.first); + inDegree[pair.first]++; + } + } + + // 拓扑排序 + std::queue q; + for (const auto& pair : inDegree) { + if (pair.second == 0) { + q.push(pair.first); + } + } + + std::vector sortedNames; + while (!q.empty()) { + std::string name = q.front(); + q.pop(); + sortedNames.push_back(name); + + for (const auto& next : graph[name]) { + inDegree[next]--; + if (inDegree[next] == 0) { + q.push(next); + } + } + } + + // 构建排序后的插件列表 + sortedPlugins_.clear(); + for (const auto& name : sortedNames) { + auto it = plugins_.find(name); + if (it != plugins_.end() && it->second.plugin) { + sortedPlugins_.push_back(it->second.plugin); + } + } +} + +void* PluginLoader::loadDynamicLibrary(const char* path) { +#ifdef _WIN32 + return LoadLibraryA(path); +#else + return dlopen(path, RTLD_LAZY); +#endif +} + +void PluginLoader::unloadDynamicLibrary(void* handle) { + if (!handle) return; + +#ifdef _WIN32 + FreeLibrary(static_cast(handle)); +#else + dlclose(handle); +#endif +} + +void* PluginLoader::getSymbol(void* handle, const char* name) { + if (!handle || !name) return nullptr; + +#ifdef _WIN32 + return reinterpret_cast(GetProcAddress(static_cast(handle), name)); +#else + return dlsym(handle, name); +#endif +} + +} // namespace extra2d diff --git a/src/utils/random.cpp b/src/utils/random.cpp index 2f96f26..7157eb6 100644 --- a/src/utils/random.cpp +++ b/src/utils/random.cpp @@ -3,66 +3,110 @@ namespace extra2d { +// ============================================================================ +// Random 类实现 +// ============================================================================ + Random::Random() : floatDist_(0.0f, 1.0f) { - // 使用当前时间作为默认种子 - randomize(); + // 使用当前时间作为默认种子 + randomize(); } -Random &Random::getInstance() { - static Random instance; - return instance; +Random::Random(uint32 seed) : floatDist_(0.0f, 1.0f) { + setSeed(seed); } -void Random::setSeed(uint32 seed) { generator_.seed(seed); } +void Random::setSeed(uint32 seed) { + generator_.seed(seed); +} void Random::randomize() { - auto now = std::chrono::high_resolution_clock::now(); - auto time = now.time_since_epoch().count(); - generator_.seed(static_cast(time)); + auto now = std::chrono::high_resolution_clock::now(); + auto time = now.time_since_epoch().count(); + generator_.seed(static_cast(time)); } -float Random::getFloat() { return floatDist_(generator_); } +float Random::getFloat() { + return floatDist_(generator_); +} float Random::getFloat(float min, float max) { - if (min >= max) { - return min; - } - return min + floatDist_(generator_) * (max - min); + if (min >= max) { + return min; + } + return min + floatDist_(generator_) * (max - min); } int Random::getInt(int max) { - if (max <= 0) { - return 0; - } - std::uniform_int_distribution dist(0, max); - return dist(generator_); + if (max <= 0) { + return 0; + } + std::uniform_int_distribution dist(0, max); + return dist(generator_); } int Random::getInt(int min, int max) { - if (min >= max) { - return min; - } - std::uniform_int_distribution dist(min, max); - return dist(generator_); + if (min >= max) { + return min; + } + std::uniform_int_distribution dist(min, max); + return dist(generator_); } -bool Random::getBool() { return floatDist_(generator_) >= 0.5f; } +bool Random::getBool() { + return floatDist_(generator_) >= 0.5f; +} bool Random::getBool(float probability) { - if (probability <= 0.0f) { - return false; - } - if (probability >= 1.0f) { - return true; - } - return floatDist_(generator_) < probability; + if (probability <= 0.0f) { + return false; + } + if (probability >= 1.0f) { + return true; + } + return floatDist_(generator_) < probability; } float Random::getAngle() { - static const float TWO_PI = 6.28318530718f; - return floatDist_(generator_) * TWO_PI; + static const float TWO_PI = 6.28318530718f; + return floatDist_(generator_) * TWO_PI; } -float Random::getSigned() { return floatDist_(generator_) * 2.0f - 1.0f; } +float Random::getSigned() { + return floatDist_(generator_) * 2.0f - 1.0f; +} + +// ============================================================================ +// 全局便捷函数 - 使用静态的默认随机数生成器 +// ============================================================================ + +static Random& getDefaultRandom() { + static Random defaultRandom; + return defaultRandom; +} + +float randomFloat() { + return getDefaultRandom().getFloat(); +} + +float randomFloat(float min, float max) { + return getDefaultRandom().getFloat(min, max); +} + +int randomInt(int max) { + return getDefaultRandom().getInt(max); +} + +int randomInt(int min, int max) { + return getDefaultRandom().getInt(min, max); +} + +bool randomBool() { + return getDefaultRandom().getBool(); +} + +bool randomBool(float probability) { + return getDefaultRandom().getBool(probability); +} } // namespace extra2d diff --git a/xmake/engine.lua b/xmake/engine.lua index 8e7d665..bf0f4e5 100644 --- a/xmake/engine.lua +++ b/xmake/engine.lua @@ -13,7 +13,7 @@ function define_extra2d_engine() target("extra2d") set_kind("static") - add_files("src/**.cpp") + add_files("src/**.cpp|core/*.cpp|module/module_manager.cpp|plugin/plugin_manager.cpp") add_files("third_party/glad/src/glad.c") add_includedirs("include", {public = true})