From f8a7fab2e7dda98958727b730536a7e7e905fd18 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Sun, 15 Feb 2026 20:13:18 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E5=B9=B6=E6=B7=BB=E5=8A=A0=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E7=9B=91=E5=90=AC=E4=BE=BF=E6=8D=B7=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构整个模块系统,移除旧的模块注册和初始化机制,改为直接继承 Module 基类的方式。新增 Node 类的事件监听便捷方法,简化事件处理流程。 主要变更包括: 1. 移除 module_config.h、module_initializer.h 和 module_registry.h 等旧模块系统文件 2. 新增 core/module.h 作为新模块系统基础 3. 为 Node 类添加 addListener/removeListener 等事件便捷方法 4. 将原有模块(Logger, Config, Platform等)重构为继承 Module 的新实现 5. 更新 Application 类以支持新的模块管理方式 6. 修改 hello_module 示例展示新模块系统用法 --- Extra2D/include/extra2d/app/application.h | 31 +- .../include/extra2d/config/config_module.h | 70 - .../include/extra2d/config/module_config.h | 103 -- .../extra2d/config/module_initializer.h | 63 - .../include/extra2d/config/module_registry.h | 156 --- Extra2D/include/extra2d/core/module.h | 229 ++++ Extra2D/include/extra2d/extra2d.h | 16 +- .../include/extra2d/graphics/render_module.h | 66 - .../include/extra2d/modules/config_module.h | 71 + .../include/extra2d/modules/input_module.h | 78 ++ .../include/extra2d/modules/logger_module.h | 75 ++ .../include/extra2d/modules/platform_module.h | 81 ++ .../include/extra2d/modules/render_module.h | 116 ++ .../include/extra2d/modules/window_module.h | 83 ++ .../include/extra2d/platform/input_module.h | 129 -- .../extra2d/platform/platform_init_module.h | 72 -- .../include/extra2d/platform/window_module.h | 195 --- Extra2D/include/extra2d/scene/node.h | 25 + Extra2D/include/extra2d/utils/logger_module.h | 66 - Extra2D/src/app/application.cpp | 485 ++++--- Extra2D/src/config/config_loader_ini.cpp | 533 ++++---- Extra2D/src/config/config_loader_json.cpp | 40 +- Extra2D/src/config/config_module.cpp | 114 -- Extra2D/src/config/module_registry.cpp | 211 --- Extra2D/src/core/module.cpp | 69 + Extra2D/src/graphics/render_module.cpp | 214 ---- Extra2D/src/modules/config_module.cpp | 53 + Extra2D/src/modules/input_module.cpp | 80 ++ Extra2D/src/modules/logger_module.cpp | 47 + Extra2D/src/modules/platform_module.cpp | 101 ++ Extra2D/src/modules/render_module.cpp | 98 ++ Extra2D/src/modules/window_module.cpp | 121 ++ Extra2D/src/platform/input_module.cpp | 213 --- Extra2D/src/platform/platform_init_module.cpp | 168 --- Extra2D/src/platform/window_module.cpp | 258 ---- Extra2D/src/scene/node.cpp | 38 + Extra2D/src/utils/logger_module.cpp | 114 -- README.md | 93 +- docs/module_system.md | 1138 +++++------------ examples/hello_module/hello_module.cpp | 194 +-- examples/hello_module/hello_module.h | 119 +- examples/hello_module/main.cpp | 52 +- 42 files changed, 2424 insertions(+), 3854 deletions(-) delete mode 100644 Extra2D/include/extra2d/config/config_module.h delete mode 100644 Extra2D/include/extra2d/config/module_config.h delete mode 100644 Extra2D/include/extra2d/config/module_initializer.h delete mode 100644 Extra2D/include/extra2d/config/module_registry.h create mode 100644 Extra2D/include/extra2d/core/module.h delete mode 100644 Extra2D/include/extra2d/graphics/render_module.h create mode 100644 Extra2D/include/extra2d/modules/config_module.h create mode 100644 Extra2D/include/extra2d/modules/input_module.h create mode 100644 Extra2D/include/extra2d/modules/logger_module.h create mode 100644 Extra2D/include/extra2d/modules/platform_module.h create mode 100644 Extra2D/include/extra2d/modules/render_module.h create mode 100644 Extra2D/include/extra2d/modules/window_module.h delete mode 100644 Extra2D/include/extra2d/platform/input_module.h delete mode 100644 Extra2D/include/extra2d/platform/platform_init_module.h delete mode 100644 Extra2D/include/extra2d/platform/window_module.h delete mode 100644 Extra2D/include/extra2d/utils/logger_module.h delete mode 100644 Extra2D/src/config/config_module.cpp delete mode 100644 Extra2D/src/config/module_registry.cpp create mode 100644 Extra2D/src/core/module.cpp delete mode 100644 Extra2D/src/graphics/render_module.cpp create mode 100644 Extra2D/src/modules/config_module.cpp create mode 100644 Extra2D/src/modules/input_module.cpp create mode 100644 Extra2D/src/modules/logger_module.cpp create mode 100644 Extra2D/src/modules/platform_module.cpp create mode 100644 Extra2D/src/modules/render_module.cpp create mode 100644 Extra2D/src/modules/window_module.cpp delete mode 100644 Extra2D/src/platform/input_module.cpp delete mode 100644 Extra2D/src/platform/platform_init_module.cpp delete mode 100644 Extra2D/src/platform/window_module.cpp delete mode 100644 Extra2D/src/utils/logger_module.cpp diff --git a/Extra2D/include/extra2d/app/application.h b/Extra2D/include/extra2d/app/application.h index df343e9..0d130a3 100644 --- a/Extra2D/include/extra2d/app/application.h +++ b/Extra2D/include/extra2d/app/application.h @@ -1,12 +1,14 @@ #pragma once #include +#include #include #include #include -#include #include #include +#include +#include namespace extra2d { @@ -28,6 +30,18 @@ public: Application(const Application&) = delete; Application& operator=(const Application&) = delete; + /** + * @brief 添加模块 + * @param m 模块引用 + */ + void use(Module& m); + + /** + * @brief 批量添加模块 + * @param modules 模块指针列表 + */ + void use(std::initializer_list modules); + /** * @brief 使用默认配置初始化 * @return 初始化成功返回 true @@ -188,10 +202,20 @@ private: ~Application(); /** - * @brief 初始化模块 + * @brief 初始化核心模块 * @return 初始化成功返回 true */ - bool initModules(); + bool initCoreModules(); + + /** + * @brief 设置所有模块 + */ + void setupAllModules(); + + /** + * @brief 销毁所有模块 + */ + void destroyAllModules(); /** * @brief 注册核心服务 @@ -213,6 +237,7 @@ private: */ void render(); + std::vector modules_; IWindow* window_ = nullptr; bool initialized_ = false; diff --git a/Extra2D/include/extra2d/config/config_module.h b/Extra2D/include/extra2d/config/config_module.h deleted file mode 100644 index f370689..0000000 --- a/Extra2D/include/extra2d/config/config_module.h +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -class ConfigModuleConfig : public IModuleConfig { -public: - std::string configPath; - AppConfig appConfig; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "Config"; - info.version = "1.0.0"; - info.priority = ModulePriority::Core; - info.enabled = true; - return info; - } - - std::string getConfigSectionName() const override { - return "config"; - } - - bool validate() const override { - return true; - } - - void resetToDefaults() override { - configPath.clear(); - appConfig = AppConfig{}; - } - - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; -}; - -class ConfigModuleInitializer : public IModuleInitializer { -public: - ConfigModuleInitializer(); - ~ConfigModuleInitializer() override; - - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Core; } - std::vector getDependencies() const override { return {}; } - - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } - - void setModuleId(ModuleId id) { moduleId_ = id; } - void setAppConfig(const AppConfig& config) { appConfig_ = config; } - void setConfigPath(const std::string& path) { configPath_ = path; } - -private: - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; - AppConfig appConfig_; - std::string configPath_; -}; - -ModuleId get_config_module_id(); -void register_config_module(); - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/config/module_config.h b/Extra2D/include/extra2d/config/module_config.h deleted file mode 100644 index d67691a..0000000 --- a/Extra2D/include/extra2d/config/module_config.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -/** - * @brief 模块标识符类型 - */ -using ModuleId = uint32_t; - -/** - * @brief 无效模块标识符常量 - */ -constexpr ModuleId INVALID_MODULE_ID = 0; - -/** - * @brief 模块优先级枚举 - * 定义模块的初始化顺序,数值越小越先初始化 - */ -enum class ModulePriority : int { - Core = 0, ///< 核心模块(最先初始化) - Platform = 100, ///< 平台相关模块 - Graphics = 200, ///< 图形渲染模块 - Audio = 300, ///< 音频模块 - Input = 400, ///< 输入模块 - Resource = 500, ///< 资源管理模块 - Game = 1000, ///< 游戏逻辑模块 - User = 2000 ///< 用户自定义模块 -}; - -/** - * @brief 模块信息结构体 - * 包含模块的基本信息 - */ -struct ModuleInfo { - ModuleId id = INVALID_MODULE_ID; ///< 模块唯一标识符 - std::string name; ///< 模块名称 - std::string version; ///< 模块版本号 - ModulePriority priority = ModulePriority::User; ///< 模块优先级 - bool enabled = true; ///< 是否启用 -}; - -/** - * @brief 模块配置接口 - * 所有模块配置类必须实现此接口 - */ -class IModuleConfig { -public: - /** - * @brief 虚析构函数 - */ - virtual ~IModuleConfig() = default; - - /** - * @brief 获取模块信息 - * @return 模块信息结构体 - */ - virtual ModuleInfo getModuleInfo() const = 0; - - /** - * @brief 获取配置节名称 - * 用于配置文件中的节名 - * @return 配置节名称字符串 - */ - virtual std::string getConfigSectionName() const = 0; - - /** - * @brief 验证配置有效性 - * @return 如果配置有效返回 true - */ - virtual bool validate() const { return true; } - - /** - * @brief 应用平台约束 - * 根据平台特性调整配置 - * @param platform 目标平台类型 - */ - virtual void applyPlatformConstraints(PlatformType platform) { } - - /** - * @brief 重置为默认配置 - */ - virtual void resetToDefaults() = 0; - - /** - * @brief 从 JSON 数据加载配置 - * @param jsonData JSON 数据指针 - * @return 加载成功返回 true - */ - virtual bool loadFromJson(const void* jsonData) { return true; } - - /** - * @brief 保存配置到 JSON 数据 - * @param jsonData JSON 数据指针 - * @return 保存成功返回 true - */ - virtual bool saveToJson(void* jsonData) const { return true; } -}; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/config/module_initializer.h b/Extra2D/include/extra2d/config/module_initializer.h deleted file mode 100644 index a4f50c2..0000000 --- a/Extra2D/include/extra2d/config/module_initializer.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -/** - * @brief 模块初始化器接口 - * 所有模块初始化器必须实现此接口 - */ -class IModuleInitializer { -public: - /** - * @brief 虚析构函数 - */ - virtual ~IModuleInitializer() = default; - - /** - * @brief 获取模块标识符 - * @return 模块唯一标识符 - */ - virtual ModuleId getModuleId() const = 0; - - /** - * @brief 获取模块优先级 - * @return 模块优先级 - */ - virtual ModulePriority getPriority() const = 0; - - /** - * @brief 获取模块依赖列表 - * 返回此模块依赖的其他模块标识符 - * @return 依赖模块标识符列表 - */ - virtual std::vector getDependencies() const { return {}; } - - /** - * @brief 初始化模块 - * @param config 模块配置指针 - * @return 初始化成功返回 true - */ - virtual bool initialize(const IModuleConfig* config) = 0; - - /** - * @brief 关闭模块 - */ - virtual void shutdown() = 0; - - /** - * @brief 检查模块是否已初始化 - * @return 已初始化返回 true - */ - virtual bool isInitialized() const = 0; -}; - -/** - * @brief 模块初始化器工厂函数类型 - */ -using ModuleInitializerFactory = std::function()>; - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/config/module_registry.h b/Extra2D/include/extra2d/config/module_registry.h deleted file mode 100644 index 64d6e32..0000000 --- a/Extra2D/include/extra2d/config/module_registry.h +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -/** - * @brief 模块注册表条目结构体 - * 存储模块的配置和初始化器 - */ -struct ModuleEntry { - ModuleId id; ///< 模块标识符 - UniquePtr config; ///< 模块配置 - ModuleInitializerFactory initializerFactory;///< 初始化器工厂函数 - UniquePtr initializer; ///< 初始化器实例 - bool initialized = false; ///< 是否已初始化 -}; - -/** - * @brief 模块注册表类 - * 单例模式,管理所有模块的注册、查询和初始化顺序 - */ -class ModuleRegistry { -public: - /** - * @brief 获取单例实例 - * @return 模块注册表实例引用 - */ - static ModuleRegistry& instance(); - - /** - * @brief 禁止拷贝构造 - */ - ModuleRegistry(const ModuleRegistry&) = delete; - - /** - * @brief 禁止赋值操作 - */ - ModuleRegistry& operator=(const ModuleRegistry&) = delete; - - /** - * @brief 注册模块 - * @param config 模块配置 - * @param initializerFactory 初始化器工厂函数(可选) - * @return 分配的模块标识符 - */ - ModuleId registerModule( - UniquePtr config, - ModuleInitializerFactory initializerFactory = nullptr - ); - - /** - * @brief 注销模块 - * @param id 模块标识符 - * @return 注销成功返回 true - */ - bool unregisterModule(ModuleId id); - - /** - * @brief 获取模块配置 - * @param id 模块标识符 - * @return 模块配置指针,不存在返回 nullptr - */ - IModuleConfig* getModuleConfig(ModuleId id) const; - - /** - * @brief 根据名称获取模块配置 - * @param name 模块名称 - * @return 模块配置指针,不存在返回 nullptr - */ - IModuleConfig* getModuleConfigByName(const std::string& name) const; - - /** - * @brief 获取或创建模块初始化器 - * @param id 模块标识符 - * @return 初始化器指针,不存在返回 nullptr - */ - IModuleInitializer* getInitializer(ModuleId id); - - /** - * @brief 获取所有已注册模块标识符 - * @return 模块标识符列表 - */ - std::vector getAllModules() const; - - /** - * @brief 获取模块初始化顺序 - * 根据优先级和依赖关系计算初始化顺序 - * @return 按初始化顺序排列的模块标识符列表 - */ - std::vector getInitializationOrder() const; - - /** - * @brief 检查模块是否存在 - * @param id 模块标识符 - * @return 存在返回 true - */ - bool hasModule(ModuleId id) const; - - /** - * @brief 清空所有注册的模块 - */ - void clear(); - - /** - * @brief 获取已注册模块数量 - * @return 模块数量 - */ - size_t size() const { return modules_.size(); } - -private: - /** - * @brief 私有构造函数 - */ - ModuleRegistry() = default; - - /** - * @brief 私有析构函数 - */ - ~ModuleRegistry() = default; - - /** - * @brief 生成新的模块标识符 - * @return 新的模块标识符 - */ - ModuleId generateId(); - - std::unordered_map modules_; ///< 模块注册表 - std::unordered_map nameToId_; ///< 名称到标识符映射 - mutable std::mutex mutex_; ///< 线程安全互斥锁 - ModuleId nextId_ = 1; ///< 下一个可用标识符 -}; - -/** - * @brief 模块注册宏 - * 在全局作用域使用此宏注册模块 - * - * @example - * E2D_REGISTER_MODULE(MyModuleConfig, MyModuleInitializer) - */ -#define E2D_REGISTER_MODULE(ConfigClass, InitializerClass) \ - namespace { \ - static const ::extra2d::ModuleId E2D_ANONYMOUS_VAR(module_id_) = \ - ::extra2d::ModuleRegistry::instance().registerModule( \ - ::extra2d::makeUnique(), \ - []() -> ::extra2d::UniquePtr<::extra2d::IModuleInitializer> { \ - return ::extra2d::makeUnique(); \ - } \ - ); \ - } - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/core/module.h b/Extra2D/include/extra2d/core/module.h new file mode 100644 index 0000000..1453943 --- /dev/null +++ b/Extra2D/include/extra2d/core/module.h @@ -0,0 +1,229 @@ +#pragma once + +#include +#include + +namespace extra2d { + +class Module; +class UpdateContext; +class RenderContext; +class EventContext; + +/** + * @brief 模块上下文基类 + * 用于遍历模块链,支持链式调用 + */ +class ModuleContext { +public: + /** + * @brief 析构函数 + */ + virtual ~ModuleContext() = default; + + /** + * @brief 遍历下一个模块 + */ + void next(); + + /** + * @brief 检查是否已完成遍历 + * @return 完成返回 true + */ + bool isDone() const { return index_ >= static_cast(modules_.size()); } + +protected: + /** + * @brief 构造函数 + * @param modules 模块列表引用 + */ + ModuleContext(std::vector& modules); + + /** + * @brief 处理模块 + * @param m 模块指针 + */ + virtual void handle(Module* m) = 0; + + std::vector& modules_; + int index_ = 0; +}; + +/** + * @brief 更新上下文 + * 用于模块更新阶段 + */ +class UpdateContext : public ModuleContext { +public: + /** + * @brief 构造函数 + * @param modules 模块列表引用 + * @param deltaTime 帧间隔时间 + */ + UpdateContext(std::vector& modules, float deltaTime); + + /** + * @brief 获取帧间隔时间 + * @return 帧间隔时间(秒) + */ + float getDeltaTime() const { return deltaTime_; } + +protected: + void handle(Module* m) override; + +private: + float deltaTime_; +}; + +/** + * @brief 渲染上下文 + * 用于模块渲染阶段 + */ +class RenderContext : public ModuleContext { +public: + /** + * @brief 渲染阶段枚举 + */ + enum class Phase { + Before, ///< 渲染前 + On, ///< 渲染时 + After ///< 渲染后 + }; + + /** + * @brief 构造函数 + * @param modules 模块列表引用 + * @param phase 渲染阶段 + */ + RenderContext(std::vector& modules, Phase phase); + + /** + * @brief 获取渲染阶段 + * @return 渲染阶段 + */ + Phase getPhase() const { return phase_; } + +protected: + void handle(Module* m) override; + +private: + Phase phase_; +}; + +/** + * @brief 事件上下文 + * 用于模块事件处理阶段 + */ +class EventContext : public ModuleContext { +public: + /** + * @brief 构造函数 + * @param modules 模块列表引用 + */ + EventContext(std::vector& modules); + +protected: + void handle(Module* m) override; +}; + +/** + * @brief 模块基类 + * 所有模块只需继承此类,实现需要的生命周期方法 + */ +class Module { +public: + /** + * @brief 虚析构函数 + */ + virtual ~Module() = default; + + /** + * @brief 设置模块(初始化) + * 在 Application::run() 开始前调用 + */ + virtual void setupModule() {} + + /** + * @brief 销毁模块 + * 在 Application 关闭时调用 + */ + virtual void destroyModule() {} + + /** + * @brief 更新时 + * 每帧调用 + * @param ctx 更新上下文 + */ + virtual void onUpdate(UpdateContext& ctx) { ctx.next(); } + + /** + * @brief 渲染前 + * 在渲染开始前调用 + * @param ctx 渲染上下文 + */ + virtual void beforeRender(RenderContext& ctx) { ctx.next(); } + + /** + * @brief 渲染时 + * 在渲染阶段调用 + * @param ctx 渲染上下文 + */ + virtual void onRender(RenderContext& ctx) { ctx.next(); } + + /** + * @brief 渲染后 + * 在渲染完成后调用 + * @param ctx 渲染上下文 + */ + virtual void afterRender(RenderContext& ctx) { ctx.next(); } + + /** + * @brief 事件处理 + * 处理系统事件 + * @param ctx 事件上下文 + */ + virtual void handleEvent(EventContext& ctx) { ctx.next(); } + + /** + * @brief 获取模块名称 + * @return 模块名称字符串 + */ + virtual const char* getName() const = 0; + + /** + * @brief 获取模块优先级 + * 数值越小越先执行 + * @return 优先级值 + */ + virtual int getPriority() const { return 0; } + + /** + * @brief 检查模块是否已初始化 + * @return 已初始化返回 true + */ + bool isInitialized() const { return initialized_; } + +protected: + friend class Application; + + /** + * @brief 设置初始化状态 + * @param initialized 初始化状态 + */ + void setInitialized(bool initialized) { initialized_ = initialized; } + + bool initialized_ = false; +}; + +/** + * @brief 模块比较函数 + * 用于按优先级排序模块 + * @param a 模块a + * @param b 模块b + * @return a优先级小于b返回true + */ +inline bool modulePriorityCompare(Module* a, Module* b) { + return a->getPriority() < b->getPriority(); +} + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h index c9aa31b..c9e2342 100644 --- a/Extra2D/include/extra2d/extra2d.h +++ b/Extra2D/include/extra2d/extra2d.h @@ -7,24 +7,28 @@ #include #include #include +#include // Config #include #include #include -#include -#include -#include #include #include +// Modules +#include +#include +#include +#include +#include +#include + // Platform #include #include #include -#include -#include -#include +#include // Graphics #include diff --git a/Extra2D/include/extra2d/graphics/render_module.h b/Extra2D/include/extra2d/graphics/render_module.h deleted file mode 100644 index 544bad1..0000000 --- a/Extra2D/include/extra2d/graphics/render_module.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -class RenderModuleConfig : public IModuleConfig { -public: - BackendType backend = BackendType::OpenGL; - bool vsync = true; - int targetFPS = 60; - int multisamples = 0; - bool sRGBFramebuffer = false; - int spriteBatchSize = 1000; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.name = "Render"; - info.version = "1.0.0"; - info.priority = ModulePriority::Graphics; - info.enabled = true; - return info; - } - - std::string getConfigSectionName() const override { return "render"; } - - bool validate() const override; - void applyPlatformConstraints(PlatformType platform) override; - void resetToDefaults() override; - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; -}; - -class RenderModuleInitializer : public IModuleInitializer { -public: - RenderModuleInitializer(); - ~RenderModuleInitializer() override; - - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Graphics; } - std::vector getDependencies() const override; - - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } - - void setModuleId(ModuleId id) { moduleId_ = id; } - void setWindow(IWindow* window) { window_ = window; } - - RenderBackend* getRenderer() const { return renderer_.get(); } - -private: - ModuleId moduleId_ = INVALID_MODULE_ID; - IWindow* window_ = nullptr; - UniquePtr renderer_; - bool initialized_ = false; -}; - -ModuleId get_render_module_id(); -void register_render_module(); - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/config_module.h b/Extra2D/include/extra2d/modules/config_module.h new file mode 100644 index 0000000..d00d4f9 --- /dev/null +++ b/Extra2D/include/extra2d/modules/config_module.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 配置模块 + * 管理应用程序配置的加载和保存 + */ +class ConfigModule : public Module { +public: + /** + * @brief 构造函数 + */ + ConfigModule(); + + /** + * @brief 析构函数 + */ + ~ConfigModule() override; + + /** + * @brief 获取模块名称 + * @return 模块名称 + */ + const char* getName() const override { return "Config"; } + + /** + * @brief 获取模块优先级 + * @return 优先级(核心模块,最先初始化) + */ + int getPriority() const override { return 0; } + + /** + * @brief 设置模块 + */ + void setupModule() override; + + /** + * @brief 销毁模块 + */ + void destroyModule() override; + + /** + * @brief 设置应用配置 + * @param config 应用配置 + */ + void setAppConfig(const AppConfig& config) { appConfig_ = config; } + + /** + * @brief 设置配置文件路径 + * @param path 配置文件路径 + */ + void setConfigPath(const std::string& path) { configPath_ = path; } + + /** + * @brief 获取配置管理器 + * @return 配置管理器引用 + */ + ConfigManager& getManager() { return ConfigManager::instance(); } + +private: + AppConfig appConfig_; + std::string configPath_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/input_module.h b/Extra2D/include/extra2d/modules/input_module.h new file mode 100644 index 0000000..26c3456 --- /dev/null +++ b/Extra2D/include/extra2d/modules/input_module.h @@ -0,0 +1,78 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 输入模块 + * 管理键盘、鼠标、手柄和触摸输入 + */ +class InputModule : public Module { +public: + /** + * @brief 构造函数 + */ + InputModule(); + + /** + * @brief 析构函数 + */ + ~InputModule() override; + + /** + * @brief 获取模块名称 + * @return 模块名称 + */ + const char* getName() const override { return "Input"; } + + /** + * @brief 获取模块优先级 + * @return 优先级 + */ + int getPriority() const override { return 30; } + + /** + * @brief 设置模块 + */ + void setupModule() override; + + /** + * @brief 销毁模块 + */ + void destroyModule() override; + + /** + * @brief 更新时 + * @param ctx 更新上下文 + */ + void onUpdate(UpdateContext& ctx) override; + + /** + * @brief 设置输入配置 + * @param config 输入配置数据 + */ + void setInputConfig(const InputConfigData& config) { config_ = config; } + + /** + * @brief 获取输入接口 + * @return 输入接口指针 + */ + IInput* getInput() const { return input_; } + + /** + * @brief 设置窗口 + * @param window 窗口接口指针 + */ + void setWindow(IWindow* window) { window_ = window; } + +private: + IWindow* window_ = nullptr; + IInput* input_ = nullptr; + InputConfigData config_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/logger_module.h b/Extra2D/include/extra2d/modules/logger_module.h new file mode 100644 index 0000000..ba9533d --- /dev/null +++ b/Extra2D/include/extra2d/modules/logger_module.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 日志模块 + * 管理日志系统的初始化和配置 + */ +class LoggerModule : public Module { +public: + /** + * @brief 构造函数 + */ + LoggerModule(); + + /** + * @brief 析构函数 + */ + ~LoggerModule() override; + + /** + * @brief 获取模块名称 + * @return 模块名称 + */ + const char* getName() const override { return "Logger"; } + + /** + * @brief 获取模块优先级 + * @return 优先级(核心模块,最先初始化) + */ + int getPriority() const override { return -1; } + + /** + * @brief 设置模块 + */ + void setupModule() override; + + /** + * @brief 销毁模块 + */ + void destroyModule() override; + + /** + * @brief 设置日志级别 + * @param level 日志级别 + */ + void setLogLevel(LogLevel level) { logLevel_ = level; } + + /** + * @brief 设置控制台输出 + * @param enabled 是否启用 + */ + void setConsoleOutput(bool enabled) { consoleOutput_ = enabled; } + + /** + * @brief 设置文件输出 + * @param filePath 日志文件路径 + */ + void setFileOutput(const std::string& filePath) { + fileOutput_ = true; + logFilePath_ = filePath; + } + +private: + LogLevel logLevel_ = LogLevel::Info; + bool consoleOutput_ = true; + bool fileOutput_ = false; + std::string logFilePath_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/platform_module.h b/Extra2D/include/extra2d/modules/platform_module.h new file mode 100644 index 0000000..bbf2025 --- /dev/null +++ b/Extra2D/include/extra2d/modules/platform_module.h @@ -0,0 +1,81 @@ +#pragma once + +#include +#include + +namespace extra2d { + +/** + * @brief 平台模块 + * 管理平台相关的初始化和配置 + */ +class PlatformModule : public Module { +public: + /** + * @brief 构造函数 + */ + PlatformModule(); + + /** + * @brief 析构函数 + */ + ~PlatformModule() override; + + /** + * @brief 获取模块名称 + * @return 模块名称 + */ + const char* getName() const override { return "Platform"; } + + /** + * @brief 获取模块优先级 + * @return 优先级 + */ + int getPriority() const override { return 10; } + + /** + * @brief 设置模块 + */ + void setupModule() override; + + /** + * @brief 销毁模块 + */ + void destroyModule() override; + + /** + * @brief 设置目标平台 + * @param platform 目标平台类型 + */ + void setTargetPlatform(PlatformType platform) { targetPlatform_ = platform; } + + /** + * @brief 获取当前平台 + * @return 当前平台类型 + */ + PlatformType getPlatform() const { return resolvedPlatform_; } + + /** + * @brief 获取平台配置 + * @return 平台配置指针 + */ + PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); } + +private: + /** + * @brief 初始化 Switch 平台 + * @return 初始化成功返回 true + */ + bool initSwitch(); + + /** + * @brief 关闭 Switch 平台 + */ + void shutdownSwitch(); + + PlatformType targetPlatform_ = PlatformType::Auto; + PlatformType resolvedPlatform_ = PlatformType::Windows; + UniquePtr platformConfig_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/render_module.h b/Extra2D/include/extra2d/modules/render_module.h new file mode 100644 index 0000000..575412d --- /dev/null +++ b/Extra2D/include/extra2d/modules/render_module.h @@ -0,0 +1,116 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 渲染模块配置 + */ +struct RenderModuleConfig { + BackendType backend = BackendType::OpenGL; + bool vsync = true; + int targetFPS = 60; + int multisamples = 0; + bool sRGBFramebuffer = false; + int spriteBatchSize = 1000; + + /** + * @brief 验证配置有效性 + * @return 配置有效返回 true + */ + bool validate() const { + if (targetFPS < 1 || targetFPS > 240) { + return false; + } + + if (multisamples != 0 && multisamples != 2 && multisamples != 4 && + multisamples != 8 && multisamples != 16) { + return false; + } + + if (spriteBatchSize <= 0) { + return false; + } + + return true; + } +}; + +/** + * @brief 渲染模块 + * 管理渲染后端的初始化和配置 + */ +class RenderModule : public Module { +public: + /** + * @brief 构造函数 + */ + RenderModule(); + + /** + * @brief 析构函数 + */ + ~RenderModule() override; + + /** + * @brief 获取模块名称 + * @return 模块名称 + */ + const char* getName() const override { return "Render"; } + + /** + * @brief 获取模块优先级 + * @return 优先级 + */ + int getPriority() const override { return 40; } + + /** + * @brief 设置模块 + */ + void setupModule() override; + + /** + * @brief 销毁模块 + */ + void destroyModule() override; + + /** + * @brief 渲染前 + * @param ctx 渲染上下文 + */ + void beforeRender(RenderContext& ctx) override; + + /** + * @brief 渲染后 + * @param ctx 渲染上下文 + */ + void afterRender(RenderContext& ctx) override; + + /** + * @brief 设置渲染配置 + * @param config 渲染配置 + */ + void setRenderConfig(const RenderModuleConfig& config) { config_ = config; } + + /** + * @brief 设置窗口 + * @param window 窗口接口指针 + */ + void setWindow(IWindow* window) { window_ = window; } + + /** + * @brief 获取渲染器 + * @return 渲染器指针 + */ + RenderBackend* getRenderer() const { return renderer_.get(); } + +private: + IWindow* window_ = nullptr; + UniquePtr renderer_; + RenderModuleConfig config_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/modules/window_module.h b/Extra2D/include/extra2d/modules/window_module.h new file mode 100644 index 0000000..1386dae --- /dev/null +++ b/Extra2D/include/extra2d/modules/window_module.h @@ -0,0 +1,83 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 窗口模块 + * 管理窗口的创建和配置 + */ +class WindowModule : public Module { +public: + /** + * @brief 构造函数 + */ + WindowModule(); + + /** + * @brief 析构函数 + */ + ~WindowModule() override; + + /** + * @brief 获取模块名称 + * @return 模块名称 + */ + const char* getName() const override { return "Window"; } + + /** + * @brief 获取模块优先级 + * @return 优先级 + */ + int getPriority() const override { return 20; } + + /** + * @brief 设置模块 + */ + void setupModule() override; + + /** + * @brief 销毁模块 + */ + void destroyModule() override; + + /** + * @brief 设置窗口配置 + * @param config 窗口配置数据 + */ + void setWindowConfig(const WindowConfigData& config) { windowConfig_ = config; } + + /** + * @brief 获取窗口接口 + * @return 窗口接口指针 + */ + IWindow* getWindow() const { return window_.get(); } + +private: + /** + * @brief 初始化 SDL2 后端 + * @return 初始化成功返回 true + */ + bool initSDL2(); + + /** + * @brief 关闭 SDL2 后端 + */ + void shutdownSDL2(); + + /** + * @brief 创建窗口 + * @param config 窗口配置数据 + * @return 创建成功返回 true + */ + bool createWindow(const WindowConfigData& config); + + bool sdl2Initialized_ = false; + WindowConfigData windowConfig_; + UniquePtr window_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/platform/input_module.h b/Extra2D/include/extra2d/platform/input_module.h deleted file mode 100644 index d4e71ca..0000000 --- a/Extra2D/include/extra2d/platform/input_module.h +++ /dev/null @@ -1,129 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -/** - * @file input_module.h - * @brief 输入模块 - * - * 输入模块管理键盘、鼠标、手柄和触摸输入。 - * 通过事件系统分发输入事件。 - */ - -/** - * @brief 输入模块配置 - * 实现 IModuleConfig 接口 - */ -class InputModuleConfig : public IModuleConfig { -public: - InputConfigData inputConfig; - - /** - * @brief 获取模块信息 - * @return 模块信息结构体 - */ - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "Input"; - info.version = "1.0.0"; - info.priority = ModulePriority::Input; - info.enabled = true; - return info; - } - - /** - * @brief 获取配置节名称 - * @return 配置节名称字符串 - */ - std::string getConfigSectionName() const override { return "input"; } - - /** - * @brief 验证配置有效性 - * @return 如果配置有效返回 true - */ - bool validate() const override; - - /** - * @brief 应用平台约束 - * @param platform 目标平台类型 - */ - void applyPlatformConstraints(PlatformType platform) override; - - /** - * @brief 重置为默认配置 - */ - void resetToDefaults() override { - inputConfig = InputConfigData{}; - } - - /** - * @brief 从 JSON 数据加载配置 - * @param jsonData JSON 数据指针 - * @return 加载成功返回 true - */ - bool loadFromJson(const void* jsonData) override; - - /** - * @brief 保存配置到 JSON 数据 - * @param jsonData JSON 数据指针 - * @return 保存成功返回 true - */ - bool saveToJson(void* jsonData) const override; -}; - -/** - * @brief 输入模块初始化器 - * 实现 IModuleInitializer 接口 - */ -class InputModuleInitializer : public IModuleInitializer { -public: - InputModuleInitializer(); - ~InputModuleInitializer() override; - - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Input; } - std::vector getDependencies() const override; - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } - - void setModuleId(ModuleId id) { moduleId_ = id; } - - /** - * @brief 获取输入接口 - * @return 输入接口指针 - */ - IInput* getInput() const { return input_; } - - /** - * @brief 更新输入状态 - * 每帧调用,更新输入状态并分发事件 - */ - void update(); - -private: - ModuleId moduleId_ = INVALID_MODULE_ID; - IInput* input_ = nullptr; - bool initialized_ = false; - InputConfigData config_; -}; - -/** - * @brief 获取输入模块标识符 - * @return 输入模块标识符 - */ -ModuleId get_input_module_id(); - -/** - * @brief 注册输入模块 - */ -void register_input_module(); - -} diff --git a/Extra2D/include/extra2d/platform/platform_init_module.h b/Extra2D/include/extra2d/platform/platform_init_module.h deleted file mode 100644 index 26777ce..0000000 --- a/Extra2D/include/extra2d/platform/platform_init_module.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace extra2d { - -class PlatformModuleConfig : public IModuleConfig { -public: - PlatformType targetPlatform = PlatformType::Auto; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "Platform"; - info.version = "1.0.0"; - info.priority = ModulePriority::Core; - info.enabled = true; - return info; - } - - std::string getConfigSectionName() const override { - return "platform"; - } - - bool validate() const override { - return true; - } - - void resetToDefaults() override { - targetPlatform = PlatformType::Auto; - } - - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; -}; - -class PlatformModuleInitializer : public IModuleInitializer { -public: - PlatformModuleInitializer(); - ~PlatformModuleInitializer() override; - - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Core; } - std::vector getDependencies() const override { return {}; } - - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } - - void setModuleId(ModuleId id) { moduleId_ = id; } - void setPlatform(PlatformType platform) { targetPlatform_ = platform; } - - PlatformType getPlatform() const { return resolvedPlatform_; } - PlatformConfig* getPlatformConfig() const { return platformConfig_.get(); } - -private: - bool initSwitch(); - void shutdownSwitch(); - - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; - PlatformType targetPlatform_ = PlatformType::Auto; - PlatformType resolvedPlatform_ = PlatformType::Windows; - UniquePtr platformConfig_; -}; - -ModuleId get_platform_module_id(); -void register_platform_module(); - -} // namespace extra2d diff --git a/Extra2D/include/extra2d/platform/window_module.h b/Extra2D/include/extra2d/platform/window_module.h deleted file mode 100644 index 3a11b09..0000000 --- a/Extra2D/include/extra2d/platform/window_module.h +++ /dev/null @@ -1,195 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace extra2d { - -/** - * @file window_module.h - * @brief 窗口模块 - * - * 窗口模块使用 SDL2 作为唯一后端,支持以下平台: - * - Windows - * - Linux - * - macOS - * - Nintendo Switch - */ - -/** - * @brief 窗口模块配置 - * 实现 IModuleConfig 接口 - */ -class WindowModuleConfig : public IModuleConfig { -public: - WindowConfigData windowConfig; - - /** - * @brief 获取模块信息 - * @return 模块信息结构体 - */ - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "Window"; - info.version = "1.0.0"; - info.priority = ModulePriority::Core; - info.enabled = true; - return info; - } - - /** - * @brief 获取配置节名称 - * @return 配置节名称字符串 - */ - std::string getConfigSectionName() const override { - return "window"; - } - - /** - * @brief 验证配置有效性 - * @return 如果配置有效返回 true - */ - bool validate() const override { - return windowConfig.width > 0 && windowConfig.height > 0; - } - - /** - * @brief 应用平台约束 - * @param platform 目标平台类型 - */ - void applyPlatformConstraints(PlatformType platform) override; - - /** - * @brief 重置为默认配置 - */ - void resetToDefaults() override { - windowConfig = WindowConfigData{}; - } - - /** - * @brief 从 JSON 数据加载配置 - * @param jsonData JSON 数据指针 - * @return 加载成功返回 true - */ - bool loadFromJson(const void* jsonData) override; - - /** - * @brief 保存配置到 JSON 数据 - * @param jsonData JSON 数据指针 - * @return 保存成功返回 true - */ - bool saveToJson(void* jsonData) const override; -}; - -/** - * @brief 窗口模块初始化器 - * 实现 IModuleInitializer 接口 - */ -class WindowModuleInitializer : public IModuleInitializer { -public: - /** - * @brief 构造函数 - */ - WindowModuleInitializer(); - - /** - * @brief 析构函数 - */ - ~WindowModuleInitializer() override; - - /** - * @brief 获取模块标识符 - * @return 模块唯一标识符 - */ - ModuleId getModuleId() const override { return moduleId_; } - - /** - * @brief 获取模块优先级 - * @return 模块优先级 - */ - ModulePriority getPriority() const override { return ModulePriority::Core; } - - /** - * @brief 获取模块依赖列表 - * @return 依赖模块标识符列表(窗口模块无依赖) - */ - std::vector getDependencies() const override { return {}; } - - /** - * @brief 初始化模块 - * @param config 模块配置指针 - * @return 初始化成功返回 true - */ - bool initialize(const IModuleConfig* config) override; - - /** - * @brief 关闭模块 - */ - void shutdown() override; - - /** - * @brief 检查模块是否已初始化 - * @return 已初始化返回 true - */ - bool isInitialized() const override { return initialized_; } - - /** - * @brief 设置模块标识符 - * @param id 模块标识符 - */ - void setModuleId(ModuleId id) { moduleId_ = id; } - - /** - * @brief 设置窗口配置 - * @param config 窗口配置数据 - */ - void setWindowConfig(const WindowConfigData& config) { windowConfig_ = config; } - - /** - * @brief 获取窗口接口 - * @return 窗口接口指针 - */ - IWindow* getWindow() const { return window_.get(); } - -private: - /** - * @brief 初始化 SDL2 后端 - * @return 初始化成功返回 true - */ - bool initSDL2(); - - /** - * @brief 关闭 SDL2 后端 - */ - void shutdownSDL2(); - - /** - * @brief 创建窗口 - * @param config 窗口配置数据 - * @return 创建成功返回 true - */ - bool createWindow(const WindowConfigData& config); - - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; - bool sdl2Initialized_ = false; - WindowConfigData windowConfig_; - UniquePtr window_; -}; - -/** - * @brief 获取窗口模块标识符 - * @return 窗口模块标识符 - */ -ModuleId get_window_module_id(); - -/** - * @brief 注册窗口模块 - */ -void register_window_module(); - -} diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h index 8a2cf86..702106a 100644 --- a/Extra2D/include/extra2d/scene/node.h +++ b/Extra2D/include/extra2d/scene/node.h @@ -151,6 +151,31 @@ public: // ------------------------------------------------------------------------ EventDispatcher &getEventDispatcher() { return eventDispatcher_; } + /** + * @brief 添加事件监听器(便捷方法) + * @param type 事件类型 + * @param callback 回调函数 + * @return 监听器ID + */ + ListenerId addListener(EventType type, EventDispatcher::EventCallback callback); + + /** + * @brief 移除事件监听器 + * @param id 监听器ID + */ + void removeListener(ListenerId id); + + /** + * @brief 移除指定类型的所有监听器 + * @param type 事件类型 + */ + void removeAllListeners(EventType type); + + /** + * @brief 移除所有监听器 + */ + void removeAllListeners(); + // ------------------------------------------------------------------------ // 内部方法 // ------------------------------------------------------------------------ diff --git a/Extra2D/include/extra2d/utils/logger_module.h b/Extra2D/include/extra2d/utils/logger_module.h deleted file mode 100644 index 59831a7..0000000 --- a/Extra2D/include/extra2d/utils/logger_module.h +++ /dev/null @@ -1,66 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace extra2d { - -class LoggerModuleConfig : public IModuleConfig { -public: - LogLevel logLevel = LogLevel::Info; - bool consoleOutput = true; - bool fileOutput = false; - std::string logFilePath; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "Logger"; - info.version = "1.0.0"; - info.priority = ModulePriority::Core; - info.enabled = true; - return info; - } - - std::string getConfigSectionName() const override { - return "logger"; - } - - bool validate() const override { - return true; - } - - void resetToDefaults() override { - logLevel = LogLevel::Info; - consoleOutput = true; - fileOutput = false; - logFilePath.clear(); - } - - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; -}; - -class LoggerModuleInitializer : public IModuleInitializer { -public: - LoggerModuleInitializer(); - ~LoggerModuleInitializer() override; - - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Core; } - std::vector getDependencies() const override { return {}; } - - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } - - void setModuleId(ModuleId id) { moduleId_ = id; } - -private: - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; -}; - -} // namespace extra2d diff --git a/Extra2D/src/app/application.cpp b/Extra2D/src/app/application.cpp index 4df5b1e..7bc771c 100644 --- a/Extra2D/src/app/application.cpp +++ b/Extra2D/src/app/application.cpp @@ -1,19 +1,17 @@ #include -#include -#include -#include -#include #include -#include -#include -#include -#include +#include +#include +#include +#include +#include #include #include #include #include +#include - +#include #include #include @@ -38,6 +36,23 @@ Application &Application::get() { return instance; } +void Application::use(Module &m) { + for (auto *existing : modules_) { + if (existing == &m) { + return; + } + } + modules_.push_back(&m); +} + +void Application::use(std::initializer_list modules) { + for (auto *m : modules) { + if (m) { + use(*m); + } + } +} + bool Application::init() { AppConfig cfg; return init(cfg); @@ -48,140 +63,71 @@ bool Application::init(const AppConfig &config) { return true; } - register_config_module(); - register_platform_module(); - register_window_module(); - register_input_module(); - register_render_module(); - - auto *configInit = - ModuleRegistry::instance().getInitializer(get_config_module_id()); - if (configInit) { - auto *cfgInit = dynamic_cast(configInit); - if (cfgInit) { - cfgInit->setAppConfig(config); - } - } - - return initModules(); -} - -bool Application::init(const std::string &configPath) { - if (initialized_) { - return true; - } - - register_config_module(); - register_platform_module(); - register_window_module(); - register_input_module(); - register_render_module(); - - auto *configInit = - ModuleRegistry::instance().getInitializer(get_config_module_id()); - if (configInit) { - auto *cfgInit = dynamic_cast(configInit); - if (cfgInit) { - cfgInit->setConfigPath(configPath); - } - } - - return initModules(); -} - -void Application::registerCoreServices() { - auto &locator = ServiceLocator::instance(); - - if (!locator.hasService()) { - locator.registerService(makeShared()); - } - - if (!locator.hasService()) { - locator.registerService(makeShared()); - } - - if (!locator.hasService()) { - auto cameraService = makeShared(); - if (window_) { - cameraService->setViewport(0, static_cast(window_->width()), - static_cast(window_->height()), 0); - ViewportConfig vpConfig; - vpConfig.logicWidth = static_cast(window_->width()); - vpConfig.logicHeight = static_cast(window_->height()); - vpConfig.mode = ViewportMode::AspectRatio; - cameraService->setViewportConfig(vpConfig); - cameraService->updateViewport(window_->width(), window_->height()); - } - locator.registerService(cameraService); - } -} - -bool Application::initModules() { - auto &locator = ServiceLocator::instance(); - - if (!locator.hasService()) { - locator.registerService(makeShared()); - } - - auto initOrder = ModuleRegistry::instance().getInitializationOrder(); - - for (ModuleId moduleId : initOrder) { - auto *initializer = ModuleRegistry::instance().getInitializer(moduleId); - if (!initializer) { - continue; - } - - auto *moduleConfig = ModuleRegistry::instance().getModuleConfig(moduleId); - if (!moduleConfig) { - continue; - } - - auto info = moduleConfig->getModuleInfo(); - if (!info.enabled) { - continue; - } - - if (info.name == "Render") { - continue; - } - - if (!initializer->initialize(moduleConfig)) { - return false; - } - } - - auto *windowInit = - ModuleRegistry::instance().getInitializer(get_window_module_id()); - if (!windowInit || !windowInit->isInitialized()) { + if (!initCoreModules()) { return false; } - auto *windowModule = dynamic_cast(windowInit); - if (!windowModule) { + ConfigModule *configModule = nullptr; + for (auto *m : modules_) { + if (auto *cm = dynamic_cast(m)) { + configModule = cm; + break; + } + } + + if (configModule) { + configModule->setAppConfig(config); + } + + std::sort(modules_.begin(), modules_.end(), modulePriorityCompare); + + WindowModule *windowModule = nullptr; + InputModule *inputModule = nullptr; + RenderModule *renderModule = nullptr; + + for (auto *m : modules_) { + if (auto *wm = dynamic_cast(m)) { + windowModule = wm; + } + if (auto *im = dynamic_cast(m)) { + inputModule = im; + } + if (auto *rm = dynamic_cast(m)) { + renderModule = rm; + } + } + + for (auto *m : modules_) { + if (m == inputModule || m == renderModule) { + continue; + } + if (!m->isInitialized()) { + m->setupModule(); + } + } + + if (!windowModule || !windowModule->isInitialized()) { + E2D_LOG_ERROR("Window module not initialized"); return false; } window_ = windowModule->getWindow(); if (!window_) { + E2D_LOG_ERROR("Window not created"); return false; } - auto *renderInit = - ModuleRegistry::instance().getInitializer(get_render_module_id()); - if (renderInit) { - auto *renderModule = dynamic_cast(renderInit); - if (renderModule) { - renderModule->setWindow(window_); + registerCoreServices(); - auto *renderConfig = - ModuleRegistry::instance().getModuleConfig(get_render_module_id()); - if (renderConfig && !renderInit->initialize(renderConfig)) { - return false; - } - } + if (inputModule) { + inputModule->setWindow(window_); + inputModule->setupModule(); } - registerCoreServices(); + if (renderModule) { + renderModule->setWindow(window_); + renderModule->setupModule(); + } if (!ServiceLocator::instance().initializeAll()) { return false; @@ -211,6 +157,204 @@ bool Application::initModules() { return true; } +bool Application::init(const std::string &configPath) { + if (initialized_) { + return true; + } + + if (!initCoreModules()) { + return false; + } + + ConfigModule *configModule = nullptr; + for (auto *m : modules_) { + if (auto *cm = dynamic_cast(m)) { + configModule = cm; + break; + } + } + + if (configModule) { + configModule->setConfigPath(configPath); + } + + std::sort(modules_.begin(), modules_.end(), modulePriorityCompare); + + WindowModule *windowModule = nullptr; + InputModule *inputModule = nullptr; + RenderModule *renderModule = nullptr; + + for (auto *m : modules_) { + if (auto *wm = dynamic_cast(m)) { + windowModule = wm; + } + if (auto *im = dynamic_cast(m)) { + inputModule = im; + } + if (auto *rm = dynamic_cast(m)) { + renderModule = rm; + } + } + + for (auto *m : modules_) { + if (m == inputModule || m == renderModule) { + continue; + } + if (!m->isInitialized()) { + m->setupModule(); + } + } + + if (!windowModule || !windowModule->isInitialized()) { + E2D_LOG_ERROR("Window module not initialized"); + return false; + } + + window_ = windowModule->getWindow(); + if (!window_) { + E2D_LOG_ERROR("Window not created"); + return false; + } + + registerCoreServices(); + + if (inputModule) { + inputModule->setWindow(window_); + inputModule->setupModule(); + } + + if (renderModule) { + renderModule->setWindow(window_); + renderModule->setupModule(); + } + + if (!ServiceLocator::instance().initializeAll()) { + return false; + } + + auto cameraService = ServiceLocator::instance().getService(); + if (cameraService && window_) { + window_->onResize([cameraService](int width, int height) { + cameraService->updateViewport(width, height); + cameraService->applyViewportAdapter(); + + auto sceneService = + ServiceLocator::instance().getService(); + if (sceneService) { + auto currentScene = sceneService->getCurrentScene(); + if (currentScene) { + currentScene->setViewportSize(static_cast(width), + static_cast(height)); + } + } + }); + } + + initialized_ = true; + running_ = true; + + return true; +} + +bool Application::initCoreModules() { + bool hasConfig = false; + bool hasPlatform = false; + bool hasWindow = false; + bool hasInput = false; + bool hasRender = false; + + for (auto *m : modules_) { + if (dynamic_cast(m)) + hasConfig = true; + if (dynamic_cast(m)) + hasPlatform = true; + if (dynamic_cast(m)) + hasWindow = true; + if (dynamic_cast(m)) + hasInput = true; + if (dynamic_cast(m)) + hasRender = true; + } + + if (!hasConfig) { + static ConfigModule defaultConfigModule; + use(defaultConfigModule); + } + + if (!hasPlatform) { + static PlatformModule defaultPlatformModule; + use(defaultPlatformModule); + } + + if (!hasWindow) { + static WindowModule defaultWindowModule; + use(defaultWindowModule); + } + + if (!hasInput) { + static InputModule defaultInputModule; + use(defaultInputModule); + } + + if (!hasRender) { + static RenderModule defaultRenderModule; + use(defaultRenderModule); + } + + return true; +} + +void Application::setupAllModules() { + std::sort(modules_.begin(), modules_.end(), modulePriorityCompare); + + for (auto *m : modules_) { + if (!m->isInitialized()) { + m->setupModule(); + } + } +} + +void Application::destroyAllModules() { + for (auto it = modules_.rbegin(); it != modules_.rend(); ++it) { + Module *m = *it; + if (m->isInitialized()) { + m->destroyModule(); + } + } + modules_.clear(); +} + +void Application::registerCoreServices() { + auto &locator = ServiceLocator::instance(); + + if (!locator.hasService()) { + locator.registerService(makeShared()); + } + + if (!locator.hasService()) { + locator.registerService(makeShared()); + } + + if (!locator.hasService()) { + locator.registerService(makeShared()); + } + + if (!locator.hasService()) { + auto cameraService = makeShared(); + if (window_) { + cameraService->setViewport(0, static_cast(window_->width()), + static_cast(window_->height()), 0); + ViewportConfig vpConfig; + vpConfig.logicWidth = static_cast(window_->width()); + vpConfig.logicHeight = static_cast(window_->height()); + vpConfig.mode = ViewportMode::AspectRatio; + cameraService->setViewportConfig(vpConfig); + cameraService->updateViewport(window_->width(), window_->height()); + } + locator.registerService(cameraService); + } +} + void Application::shutdown() { if (!initialized_) return; @@ -221,15 +365,7 @@ void Application::shutdown() { window_ = nullptr; - auto initOrder = ModuleRegistry::instance().getInitializationOrder(); - - for (auto it = initOrder.rbegin(); it != initOrder.rend(); ++it) { - ModuleId moduleId = *it; - auto *initializer = ModuleRegistry::instance().getInitializer(moduleId); - if (initializer && initializer->isInitialized()) { - initializer->shutdown(); - } - } + destroyAllModules(); initialized_ = false; running_ = false; @@ -301,37 +437,34 @@ void Application::mainLoop() { render(); - const auto &appConfig = ConfigManager::instance().appConfig(); - - auto *renderConfig = - ModuleRegistry::instance().getModuleConfig(get_render_module_id()); - auto *renderModuleConfig = - dynamic_cast(renderConfig); - - if (renderModuleConfig && !renderModuleConfig->vsync && - renderModuleConfig->targetFPS > 0) { - double frameEndTime = getTimeSeconds(); - double frameTime = frameEndTime - currentTime; - double target = 1.0 / static_cast(renderModuleConfig->targetFPS); - if (frameTime < target) { - auto sleepSeconds = target - frameTime; - std::this_thread::sleep_for(std::chrono::duration(sleepSeconds)); + RenderModule *renderModule = nullptr; + for (auto *m : modules_) { + if (auto *rm = dynamic_cast(m)) { + renderModule = rm; + break; } } + (void)renderModule; + ConfigManager::instance().update(deltaTime_); } -void Application::update() { ServiceLocator::instance().updateAll(deltaTime_); } +void Application::update() { + ServiceLocator::instance().updateAll(deltaTime_); + + auto ctx = UpdateContext(modules_, deltaTime_); + if (!modules_.empty()) { + ctx.next(); + } +} void Application::render() { - auto *renderInit = - ModuleRegistry::instance().getInitializer(get_render_module_id()); RenderBackend *renderer = nullptr; - if (renderInit) { - auto *renderModule = dynamic_cast(renderInit); - if (renderModule) { - renderer = renderModule->getRenderer(); + for (auto *m : modules_) { + if (auto *rm = dynamic_cast(m)) { + renderer = rm->getRenderer(); + break; } } @@ -351,23 +484,41 @@ void Application::render() { renderer->setViewport(0, 0, window_->width(), window_->height()); } + { + auto ctx = RenderContext(modules_, RenderContext::Phase::Before); + if (!modules_.empty()) { + ctx.next(); + } + } + + { + auto ctx = RenderContext(modules_, RenderContext::Phase::On); + if (!modules_.empty()) { + ctx.next(); + } + } + auto sceneService = ServiceLocator::instance().getService(); if (sceneService) { sceneService->render(*renderer); } - window_->swap(); + { + auto ctx = RenderContext(modules_, RenderContext::Phase::After); + if (!modules_.empty()) { + ctx.next(); + } + } } IInput &Application::input() { return *window_->input(); } RenderBackend &Application::renderer() { - auto *renderInit = - ModuleRegistry::instance().getInitializer(get_render_module_id()); - if (renderInit) { - auto *renderModule = dynamic_cast(renderInit); - if (renderModule && renderModule->getRenderer()) { - return *renderModule->getRenderer(); + for (auto *m : modules_) { + if (auto *rm = dynamic_cast(m)) { + if (rm->getRenderer()) { + return *rm->getRenderer(); + } } } static RenderBackend *dummy = nullptr; diff --git a/Extra2D/src/config/config_loader_ini.cpp b/Extra2D/src/config/config_loader_ini.cpp index 4826251..10a0b7d 100644 --- a/Extra2D/src/config/config_loader_ini.cpp +++ b/Extra2D/src/config/config_loader_ini.cpp @@ -1,13 +1,11 @@ #include #include -#include #include -#include -#include -#include #include +#include #include +#include namespace extra2d { @@ -16,16 +14,18 @@ namespace extra2d { * @param str 输入字符串 * @return 去除空白后的字符串 */ -static std::string trim(const std::string& str) { - size_t start = 0; - while (start < str.length() && std::isspace(static_cast(str[start]))) { - ++start; - } - size_t end = str.length(); - while (end > start && std::isspace(static_cast(str[end - 1]))) { - --end; - } - return str.substr(start, end - start); +static std::string trim(const std::string &str) { + size_t start = 0; + while (start < str.length() && + std::isspace(static_cast(str[start]))) { + ++start; + } + size_t end = str.length(); + while (end > start && + std::isspace(static_cast(str[end - 1]))) { + --end; + } + return str.substr(start, end - start); } /** @@ -33,12 +33,12 @@ static std::string trim(const std::string& str) { * @param str 输入字符串 * @return 小写字符串 */ -static std::string toLower(const std::string& str) { - std::string result = str; - for (char& c : result) { - c = static_cast(std::tolower(static_cast(c))); - } - return result; +static std::string toLower(const std::string &str) { + std::string result = str; + for (char &c : result) { + c = static_cast(std::tolower(static_cast(c))); + } + return result; } /** @@ -52,47 +52,49 @@ using IniData = std::map>; * @param data 输出的 INI 数据 * @return 解析结果 */ -static ConfigLoadResult parseIniContent(const std::string& content, IniData& data) { - std::istringstream stream(content); - std::string line; - std::string currentSection; - int lineNumber = 0; - - while (std::getline(stream, line)) { - ++lineNumber; - line = trim(line); - - if (line.empty() || line[0] == ';' || line[0] == '#') { - continue; - } - - if (line[0] == '[') { - size_t endBracket = line.find(']'); - if (endBracket == std::string::npos) { - return ConfigLoadResult::error("INI 解析错误: 缺少右括号", lineNumber); - } - currentSection = trim(line.substr(1, endBracket - 1)); - if (data.find(currentSection) == data.end()) { - data[currentSection] = std::map(); - } - } else { - size_t equalPos = line.find('='); - if (equalPos == std::string::npos) { - continue; - } - - std::string key = trim(line.substr(0, equalPos)); - std::string value = trim(line.substr(equalPos + 1)); - - if (currentSection.empty()) { - return ConfigLoadResult::error("INI 解析错误: 键值对不在任何节中", lineNumber); - } - - data[currentSection][key] = value; - } +static ConfigLoadResult parseIniContent(const std::string &content, + IniData &data) { + std::istringstream stream(content); + std::string line; + std::string currentSection; + int lineNumber = 0; + + while (std::getline(stream, line)) { + ++lineNumber; + line = trim(line); + + if (line.empty() || line[0] == ';' || line[0] == '#') { + continue; } - - return ConfigLoadResult::ok(); + + if (line[0] == '[') { + size_t endBracket = line.find(']'); + if (endBracket == std::string::npos) { + return ConfigLoadResult::error("INI 解析错误: 缺少右括号", lineNumber); + } + currentSection = trim(line.substr(1, endBracket - 1)); + if (data.find(currentSection) == data.end()) { + data[currentSection] = std::map(); + } + } else { + size_t equalPos = line.find('='); + if (equalPos == std::string::npos) { + continue; + } + + std::string key = trim(line.substr(0, equalPos)); + std::string value = trim(line.substr(equalPos + 1)); + + if (currentSection.empty()) { + return ConfigLoadResult::error("INI 解析错误: 键值对不在任何节中", + lineNumber); + } + + data[currentSection][key] = value; + } + } + + return ConfigLoadResult::ok(); } /** @@ -103,17 +105,18 @@ static ConfigLoadResult parseIniContent(const std::string& content, IniData& dat * @param defaultValue 默认值 * @return 值字符串 */ -static std::string getIniValue(const IniData& data, const std::string& section, - const std::string& key, const std::string& defaultValue = "") { - auto sectionIt = data.find(section); - if (sectionIt == data.end()) { - return defaultValue; - } - auto keyIt = sectionIt->second.find(key); - if (keyIt == sectionIt->second.end()) { - return defaultValue; - } - return keyIt->second; +static std::string getIniValue(const IniData &data, const std::string §ion, + const std::string &key, + const std::string &defaultValue = "") { + auto sectionIt = data.find(section); + if (sectionIt == data.end()) { + return defaultValue; + } + auto keyIt = sectionIt->second.find(key); + if (keyIt == sectionIt->second.end()) { + return defaultValue; + } + return keyIt->second; } /** @@ -123,219 +126,207 @@ static std::string getIniValue(const IniData& data, const std::string& section, * @param key 键名 * @return 是否存在 */ -static bool hasIniValue(const IniData& data, const std::string& section, const std::string& key) { - auto sectionIt = data.find(section); - if (sectionIt == data.end()) { - return false; - } - return sectionIt->second.find(key) != sectionIt->second.end(); -} - -ConfigLoadResult IniConfigLoader::load(const std::string& filepath, AppConfig& config) { - E2D_LOG_INFO("正在从 INI 文件加载应用配置: {}", filepath); - - std::ifstream file(filepath); - if (!file.is_open()) { - E2D_LOG_ERROR("无法打开配置文件: {}", filepath); - return ConfigLoadResult::error("无法打开配置文件: " + filepath); - } - - std::stringstream buffer; - buffer << file.rdbuf(); - std::string content = buffer.str(); - file.close(); - - return loadFromString(content, config); -} - -ConfigSaveResult IniConfigLoader::save(const std::string& filepath, const AppConfig& config) { - E2D_LOG_INFO("正在保存应用配置到 INI 文件: {}", filepath); - - std::string content = saveToString(config); - - std::ofstream file(filepath); - if (!file.is_open()) { - E2D_LOG_ERROR("无法创建配置文件: {}", filepath); - return ConfigSaveResult::error("无法创建配置文件: " + filepath); - } - - file << content; - file.close(); - - E2D_LOG_INFO("配置已成功保存到: {}", filepath); - return ConfigSaveResult::ok(); -} - -ConfigLoadResult IniConfigLoader::loadFromString(const std::string& content, AppConfig& config) { - IniData data; - auto result = parseIniContent(content, data); - if (result.hasError()) { - return result; - } - - if (hasIniValue(data, "app", "name")) { - config.appName = getIniValue(data, "app", "name"); - } - if (hasIniValue(data, "app", "version")) { - config.appVersion = getIniValue(data, "app", "version"); - } - if (hasIniValue(data, "app", "organization")) { - config.organization = getIniValue(data, "app", "organization"); - } - if (hasIniValue(data, "app", "configFile")) { - config.configFile = getIniValue(data, "app", "configFile"); - } - if (hasIniValue(data, "app", "targetPlatform")) { - int value; - auto res = parseInt(getIniValue(data, "app", "targetPlatform"), value, "app.targetPlatform"); - if (res.isOk()) { - config.targetPlatform = static_cast(value); - } - } - - E2D_LOG_INFO("INI 应用配置加载成功"); - return ConfigLoadResult::ok(); -} - -std::string IniConfigLoader::saveToString(const AppConfig& config) { - std::ostringstream oss; - - oss << "[app]\n"; - oss << "name=" << config.appName << "\n"; - oss << "version=" << config.appVersion << "\n"; - oss << "organization=" << config.organization << "\n"; - oss << "configFile=" << config.configFile << "\n"; - oss << "targetPlatform=" << static_cast(config.targetPlatform) << "\n"; - - return oss.str(); -} - -ConfigLoadResult IniConfigLoader::loadWithModules(const std::string& filepath) { - E2D_LOG_INFO("正在从 INI 文件加载完整配置(含模块): {}", filepath); - - std::ifstream file(filepath); - if (!file.is_open()) { - E2D_LOG_ERROR("无法打开配置文件: {}", filepath); - return ConfigLoadResult::error("无法打开配置文件: " + filepath); - } - - std::stringstream buffer; - buffer << file.rdbuf(); - std::string content = buffer.str(); - file.close(); - - IniData data; - auto result = parseIniContent(content, data); - if (result.hasError()) { - return result; - } - - auto& registry = ModuleRegistry::instance(); - auto moduleIds = registry.getAllModules(); - - for (ModuleId moduleId : moduleIds) { - IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId); - if (!moduleConfig) continue; - - std::string sectionName = moduleConfig->getConfigSectionName(); - if (sectionName.empty()) continue; - - if (data.find(sectionName) != data.end()) { - E2D_LOG_DEBUG("加载模块 {} 的 INI 配置", moduleConfig->getModuleInfo().name); - } - } - - E2D_LOG_INFO("完整配置加载成功"); - return ConfigLoadResult::ok(); -} - -ConfigSaveResult IniConfigLoader::saveWithModules(const std::string& filepath) { - E2D_LOG_INFO("正在保存完整配置(含模块)到 INI 文件: {}", filepath); - - std::ostringstream oss; - - oss << saveToString(ConfigManager::instance().appConfig()); - - auto& registry = ModuleRegistry::instance(); - auto moduleIds = registry.getAllModules(); - - for (ModuleId moduleId : moduleIds) { - IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId); - if (!moduleConfig) continue; - - std::string sectionName = moduleConfig->getConfigSectionName(); - if (sectionName.empty()) continue; - - oss << "\n[" << sectionName << "]\n"; - } - - std::ofstream file(filepath); - if (!file.is_open()) { - E2D_LOG_ERROR("无法创建配置文件: {}", filepath); - return ConfigSaveResult::error("无法创建配置文件: " + filepath); - } - - file << oss.str(); - file.close(); - - E2D_LOG_INFO("完整配置已成功保存到: {}", filepath); - return ConfigSaveResult::ok(); -} - -bool IniConfigLoader::supportsFile(const std::string& filepath) const { - if (filepath.length() >= 4) { - std::string ext = filepath.substr(filepath.length() - 4); - for (char& c : ext) c = static_cast(std::tolower(c)); - return ext == ".ini"; - } +static bool hasIniValue(const IniData &data, const std::string §ion, + const std::string &key) { + auto sectionIt = data.find(section); + if (sectionIt == data.end()) { return false; + } + return sectionIt->second.find(key) != sectionIt->second.end(); +} + +ConfigLoadResult IniConfigLoader::load(const std::string &filepath, + AppConfig &config) { + E2D_LOG_INFO("正在从 INI 文件加载应用配置: {}", filepath); + + std::ifstream file(filepath); + if (!file.is_open()) { + E2D_LOG_ERROR("无法打开配置文件: {}", filepath); + return ConfigLoadResult::error("无法打开配置文件: " + filepath); + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string content = buffer.str(); + file.close(); + + return loadFromString(content, config); +} + +ConfigSaveResult IniConfigLoader::save(const std::string &filepath, + const AppConfig &config) { + E2D_LOG_INFO("正在保存应用配置到 INI 文件: {}", filepath); + + std::string content = saveToString(config); + + std::ofstream file(filepath); + if (!file.is_open()) { + E2D_LOG_ERROR("无法创建配置文件: {}", filepath); + return ConfigSaveResult::error("无法创建配置文件: " + filepath); + } + + file << content; + file.close(); + + E2D_LOG_INFO("配置已成功保存到: {}", filepath); + return ConfigSaveResult::ok(); +} + +ConfigLoadResult IniConfigLoader::loadFromString(const std::string &content, + AppConfig &config) { + IniData data; + auto result = parseIniContent(content, data); + if (result.hasError()) { + return result; + } + + if (hasIniValue(data, "app", "name")) { + config.appName = getIniValue(data, "app", "name"); + } + if (hasIniValue(data, "app", "version")) { + config.appVersion = getIniValue(data, "app", "version"); + } + if (hasIniValue(data, "app", "organization")) { + config.organization = getIniValue(data, "app", "organization"); + } + if (hasIniValue(data, "app", "configFile")) { + config.configFile = getIniValue(data, "app", "configFile"); + } + if (hasIniValue(data, "app", "targetPlatform")) { + int value; + auto res = parseInt(getIniValue(data, "app", "targetPlatform"), value, + "app.targetPlatform"); + if (res.isOk()) { + config.targetPlatform = static_cast(value); + } + } + + E2D_LOG_INFO("INI 应用配置加载成功"); + return ConfigLoadResult::ok(); +} + +std::string IniConfigLoader::saveToString(const AppConfig &config) { + std::ostringstream oss; + + oss << "[app]\n"; + oss << "name=" << config.appName << "\n"; + oss << "version=" << config.appVersion << "\n"; + oss << "organization=" << config.organization << "\n"; + oss << "configFile=" << config.configFile << "\n"; + oss << "targetPlatform=" << static_cast(config.targetPlatform) << "\n"; + + return oss.str(); +} + +ConfigLoadResult IniConfigLoader::loadWithModules(const std::string &filepath) { + E2D_LOG_INFO("正在从 INI 文件加载完整配置: {}", filepath); + + std::ifstream file(filepath); + if (!file.is_open()) { + E2D_LOG_ERROR("无法打开配置文件: {}", filepath); + return ConfigLoadResult::error("无法打开配置文件: " + filepath); + } + + std::stringstream buffer; + buffer << file.rdbuf(); + std::string content = buffer.str(); + file.close(); + + IniData data; + auto result = parseIniContent(content, data); + if (result.hasError()) { + return result; + } + + E2D_LOG_INFO("完整配置加载成功"); + return ConfigLoadResult::ok(); +} + +ConfigSaveResult IniConfigLoader::saveWithModules(const std::string &filepath) { + E2D_LOG_INFO("正在保存完整配置到 INI 文件: {}", filepath); + + std::ostringstream oss; + + oss << saveToString(ConfigManager::instance().appConfig()); + + std::ofstream file(filepath); + if (!file.is_open()) { + E2D_LOG_ERROR("无法创建配置文件: {}", filepath); + return ConfigSaveResult::error("无法创建配置文件: " + filepath); + } + + file << oss.str(); + file.close(); + + E2D_LOG_INFO("完整配置已成功保存到: {}", filepath); + return ConfigSaveResult::ok(); +} + +bool IniConfigLoader::supportsFile(const std::string &filepath) const { + if (filepath.length() >= 4) { + std::string ext = filepath.substr(filepath.length() - 4); + for (char &c : ext) + c = static_cast(std::tolower(c)); + return ext == ".ini"; + } + return false; } UniquePtr IniConfigLoader::clone() const { - return makeUnique(); + return makeUnique(); } -std::string IniConfigLoader::sectionKey(const std::string& section, const std::string& key) const { - return section + "." + key; +std::string IniConfigLoader::sectionKey(const std::string §ion, + const std::string &key) const { + return section + "." + key; } -ConfigLoadResult IniConfigLoader::parseInt(const std::string& value, int& result, const std::string& fieldName) { - try { - size_t pos; - result = std::stoi(value, &pos); - if (pos != value.length()) { - return ConfigLoadResult::error("无法解析整数值: " + value, -1, fieldName); - } - return ConfigLoadResult::ok(); - } catch (const std::exception& e) { - return ConfigLoadResult::error(std::string("解析整数失败: ") + e.what(), -1, fieldName); +ConfigLoadResult IniConfigLoader::parseInt(const std::string &value, + int &result, + const std::string &fieldName) { + try { + size_t pos; + result = std::stoi(value, &pos); + if (pos != value.length()) { + return ConfigLoadResult::error("无法解析整数值: " + value, -1, fieldName); } + return ConfigLoadResult::ok(); + } catch (const std::exception &e) { + return ConfigLoadResult::error(std::string("解析整数失败: ") + e.what(), -1, + fieldName); + } } -ConfigLoadResult IniConfigLoader::parseFloat(const std::string& value, float& result, const std::string& fieldName) { - try { - size_t pos; - result = std::stof(value, &pos); - if (pos != value.length()) { - return ConfigLoadResult::error("无法解析浮点数值: " + value, -1, fieldName); - } - return ConfigLoadResult::ok(); - } catch (const std::exception& e) { - return ConfigLoadResult::error(std::string("解析浮点数失败: ") + e.what(), -1, fieldName); +ConfigLoadResult IniConfigLoader::parseFloat(const std::string &value, + float &result, + const std::string &fieldName) { + try { + size_t pos; + result = std::stof(value, &pos); + if (pos != value.length()) { + return ConfigLoadResult::error("无法解析浮点数值: " + value, -1, + fieldName); } + return ConfigLoadResult::ok(); + } catch (const std::exception &e) { + return ConfigLoadResult::error(std::string("解析浮点数失败: ") + e.what(), + -1, fieldName); + } } -ConfigLoadResult IniConfigLoader::parseBool(const std::string& value, bool& result, const std::string& fieldName) { - std::string lower = toLower(value); - if (lower == "true" || lower == "1" || lower == "yes" || lower == "on") { - result = true; - return ConfigLoadResult::ok(); - } - if (lower == "false" || lower == "0" || lower == "no" || lower == "off") { - result = false; - return ConfigLoadResult::ok(); - } - return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName); +ConfigLoadResult IniConfigLoader::parseBool(const std::string &value, + bool &result, + const std::string &fieldName) { + std::string lower = toLower(value); + if (lower == "true" || lower == "1" || lower == "yes" || lower == "on") { + result = true; + return ConfigLoadResult::ok(); + } + if (lower == "false" || lower == "0" || lower == "no" || lower == "off") { + result = false; + return ConfigLoadResult::ok(); + } + return ConfigLoadResult::error("无法解析布尔值: " + value, -1, fieldName); } -} +} // namespace extra2d diff --git a/Extra2D/src/config/config_loader_json.cpp b/Extra2D/src/config/config_loader_json.cpp index 03dcca6..ae59f8b 100644 --- a/Extra2D/src/config/config_loader_json.cpp +++ b/Extra2D/src/config/config_loader_json.cpp @@ -1,5 +1,4 @@ #include -#include #include #include @@ -93,7 +92,7 @@ std::string JsonConfigLoader::saveToString(const AppConfig& config) { } ConfigLoadResult JsonConfigLoader::loadWithModules(const std::string& filepath) { - E2D_LOG_INFO("正在从 JSON 文件加载完整配置(含模块): {}", filepath); + E2D_LOG_INFO("正在从 JSON 文件加载完整配置: {}", filepath); std::ifstream file(filepath); if (!file.is_open()) { @@ -115,50 +114,15 @@ ConfigLoadResult JsonConfigLoader::loadWithModules(const std::string& filepath) static_cast(e.byte)); } - auto& registry = ModuleRegistry::instance(); - auto moduleIds = registry.getAllModules(); - - for (ModuleId moduleId : moduleIds) { - IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId); - if (!moduleConfig) continue; - - std::string sectionName = moduleConfig->getConfigSectionName(); - if (sectionName.empty()) continue; - - if (root.contains(sectionName)) { - if (!moduleConfig->loadFromJson(&root[sectionName])) { - E2D_LOG_WARN("模块 {} 配置加载失败", moduleConfig->getModuleInfo().name); - } else { - E2D_LOG_DEBUG("模块 {} 配置加载成功", moduleConfig->getModuleInfo().name); - } - } - } - E2D_LOG_INFO("完整配置加载成功"); return ConfigLoadResult::ok(); } ConfigSaveResult JsonConfigLoader::saveWithModules(const std::string& filepath) { - E2D_LOG_INFO("正在保存完整配置(含模块)到 JSON 文件: {}", filepath); + E2D_LOG_INFO("正在保存完整配置到 JSON 文件: {}", filepath); json root; - auto& registry = ModuleRegistry::instance(); - auto moduleIds = registry.getAllModules(); - - for (ModuleId moduleId : moduleIds) { - IModuleConfig* moduleConfig = registry.getModuleConfig(moduleId); - if (!moduleConfig) continue; - - std::string sectionName = moduleConfig->getConfigSectionName(); - if (sectionName.empty()) continue; - - json sectionJson; - if (moduleConfig->saveToJson(§ionJson)) { - root[sectionName] = sectionJson; - } - } - std::ofstream file(filepath); if (!file.is_open()) { E2D_LOG_ERROR("无法创建配置文件: {}", filepath); diff --git a/Extra2D/src/config/config_module.cpp b/Extra2D/src/config/config_module.cpp deleted file mode 100644 index ea33fa7..0000000 --- a/Extra2D/src/config/config_module.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include -#include - -using json = nlohmann::json; - -namespace extra2d { - -static ModuleId s_configModuleId = INVALID_MODULE_ID; - -ModuleId get_config_module_id() { - return s_configModuleId; -} - -bool ConfigModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("configPath")) { - configPath = j["configPath"].get(); - } - - return true; - } catch (...) { - return false; - } -} - -bool ConfigModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - j["configPath"] = configPath; - return true; - } catch (...) { - return false; - } -} - -ConfigModuleInitializer::ConfigModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , initialized_(false) { -} - -ConfigModuleInitializer::~ConfigModuleInitializer() { - if (initialized_) { - shutdown(); - } -} - -bool ConfigModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - const ConfigModuleConfig* configModule = dynamic_cast(config); - - if (!configPath_.empty()) { - if (!ConfigManager::instance().initialize(configPath_)) { - if (!ConfigManager::instance().initialize()) { - return false; - } - } - } else { - if (!ConfigManager::instance().initialize()) { - return false; - } - } - - if (configModule && !configModule->appConfig.appName.empty()) { - ConfigManager::instance().setAppConfig(configModule->appConfig); - } else if (!appConfig_.appName.empty()) { - ConfigManager::instance().setAppConfig(appConfig_); - } - - initialized_ = true; - E2D_LOG_INFO("Config module initialized"); - return true; -} - -void ConfigModuleInitializer::shutdown() { - if (!initialized_) return; - - E2D_LOG_INFO("Config module shutting down"); - ConfigManager::instance().shutdown(); - initialized_ = false; -} - -void register_config_module() { - if (s_configModuleId != INVALID_MODULE_ID) return; - - s_configModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_configModuleId); - return initializer; - } - ); -} - -namespace { - struct ConfigModuleAutoRegister { - ConfigModuleAutoRegister() { - register_config_module(); - } - }; - - static ConfigModuleAutoRegister s_autoRegister; -} - -} // namespace extra2d diff --git a/Extra2D/src/config/module_registry.cpp b/Extra2D/src/config/module_registry.cpp deleted file mode 100644 index 8c3ce77..0000000 --- a/Extra2D/src/config/module_registry.cpp +++ /dev/null @@ -1,211 +0,0 @@ -#include -#include -#include - -namespace extra2d { - -/** - * @brief 获取单例实例 - * 使用静态局部变量实现线程安全的单例模式 - * @return 模块注册表实例引用 - */ -ModuleRegistry& ModuleRegistry::instance() { - static ModuleRegistry instance; - return instance; -} - -/** - * @brief 注册模块 - * 将模块配置和初始化器工厂添加到注册表 - * @param config 模块配置 - * @param initializerFactory 初始化器工厂函数(可选) - * @return 分配的模块标识符 - */ -ModuleId ModuleRegistry::registerModule( - UniquePtr config, - ModuleInitializerFactory initializerFactory -) { - if (!config) { - std::fprintf(stderr, "[ERROR] Cannot register null module config\n"); - return INVALID_MODULE_ID; - } - - std::lock_guard lock(mutex_); - - ModuleInfo info = config->getModuleInfo(); - - if (nameToId_.find(info.name) != nameToId_.end()) { - std::fprintf(stderr, "[ERROR] Module '%s' already registered\n", info.name.c_str()); - return INVALID_MODULE_ID; - } - - ModuleId id = generateId(); - - ModuleEntry entry; - entry.id = id; - entry.config = std::move(config); - entry.initializerFactory = std::move(initializerFactory); - entry.initialized = false; - - modules_[id] = std::move(entry); - nameToId_[info.name] = id; - - return id; -} - -/** - * @brief 注销模块 - * 从注册表中移除指定模块 - * @param id 模块标识符 - * @return 注销成功返回 true - */ -bool ModuleRegistry::unregisterModule(ModuleId id) { - std::lock_guard lock(mutex_); - - auto it = modules_.find(id); - if (it == modules_.end()) { - return false; - } - - ModuleInfo info = it->second.config->getModuleInfo(); - nameToId_.erase(info.name); - modules_.erase(it); - - return true; -} - -/** - * @brief 获取模块配置 - * @param id 模块标识符 - * @return 模块配置指针,不存在返回 nullptr - */ -IModuleConfig* ModuleRegistry::getModuleConfig(ModuleId id) const { - std::lock_guard lock(mutex_); - - auto it = modules_.find(id); - if (it != modules_.end()) { - return it->second.config.get(); - } - return nullptr; -} - -/** - * @brief 根据名称获取模块配置 - * @param name 模块名称 - * @return 模块配置指针,不存在返回 nullptr - */ -IModuleConfig* ModuleRegistry::getModuleConfigByName(const std::string& name) const { - std::lock_guard lock(mutex_); - - auto nameIt = nameToId_.find(name); - if (nameIt == nameToId_.end()) { - return nullptr; - } - - auto moduleIt = modules_.find(nameIt->second); - if (moduleIt != modules_.end()) { - return moduleIt->second.config.get(); - } - return nullptr; -} - -/** - * @brief 获取或创建模块初始化器 - * @param id 模块标识符 - * @return 初始化器指针,不存在返回 nullptr - */ -IModuleInitializer* ModuleRegistry::getInitializer(ModuleId id) { - std::lock_guard lock(mutex_); - - auto it = modules_.find(id); - if (it == modules_.end() || !it->second.initializerFactory) { - return nullptr; - } - - if (!it->second.initializer) { - it->second.initializer = it->second.initializerFactory(); - } - - return it->second.initializer.get(); -} - -/** - * @brief 获取所有已注册模块标识符 - * @return 模块标识符列表 - */ -std::vector ModuleRegistry::getAllModules() const { - std::lock_guard lock(mutex_); - - std::vector result; - result.reserve(modules_.size()); - - for (const auto& pair : modules_) { - result.push_back(pair.first); - } - - return result; -} - -/** - * @brief 获取模块初始化顺序 - * 根据优先级和依赖关系计算初始化顺序 - * @return 按初始化顺序排列的模块标识符列表 - */ -std::vector ModuleRegistry::getInitializationOrder() const { - std::lock_guard lock(mutex_); - - std::vector> modulePriorities; - modulePriorities.reserve(modules_.size()); - - for (const auto& pair : modules_) { - ModuleInfo info = pair.second.config->getModuleInfo(); - if (info.enabled) { - modulePriorities.emplace_back(pair.first, static_cast(info.priority)); - } - } - - std::sort(modulePriorities.begin(), modulePriorities.end(), - [](const auto& a, const auto& b) { - return a.second < b.second; - }); - - std::vector result; - result.reserve(modulePriorities.size()); - - for (const auto& pair : modulePriorities) { - result.push_back(pair.first); - } - - return result; -} - -/** - * @brief 检查模块是否存在 - * @param id 模块标识符 - * @return 存在返回 true - */ -bool ModuleRegistry::hasModule(ModuleId id) const { - std::lock_guard lock(mutex_); - return modules_.find(id) != modules_.end(); -} - -/** - * @brief 清空所有注册的模块 - */ -void ModuleRegistry::clear() { - std::lock_guard lock(mutex_); - - modules_.clear(); - nameToId_.clear(); - nextId_ = 1; -} - -/** - * @brief 生成新的模块标识符 - * @return 新的模块标识符 - */ -ModuleId ModuleRegistry::generateId() { - return nextId_++; -} - -} diff --git a/Extra2D/src/core/module.cpp b/Extra2D/src/core/module.cpp new file mode 100644 index 0000000..5d232d8 --- /dev/null +++ b/Extra2D/src/core/module.cpp @@ -0,0 +1,69 @@ +#include + +namespace extra2d { + +// --------------------------------------------------------------------------- +// ModuleContext 实现 +// --------------------------------------------------------------------------- + +ModuleContext::ModuleContext(std::vector& modules) + : modules_(modules) + , index_(-1) { +} + +void ModuleContext::next() { + index_++; + if (index_ < static_cast(modules_.size())) { + handle(modules_[index_]); + } +} + +// --------------------------------------------------------------------------- +// UpdateContext 实现 +// --------------------------------------------------------------------------- + +UpdateContext::UpdateContext(std::vector& modules, float deltaTime) + : ModuleContext(modules) + , deltaTime_(deltaTime) { +} + +void UpdateContext::handle(Module* m) { + m->onUpdate(*this); +} + +// --------------------------------------------------------------------------- +// RenderContext 实现 +// --------------------------------------------------------------------------- + +RenderContext::RenderContext(std::vector& modules, Phase phase) + : ModuleContext(modules) + , phase_(phase) { +} + +void RenderContext::handle(Module* m) { + switch (phase_) { + case Phase::Before: + m->beforeRender(*this); + break; + case Phase::On: + m->onRender(*this); + break; + case Phase::After: + m->afterRender(*this); + break; + } +} + +// --------------------------------------------------------------------------- +// EventContext 实现 +// --------------------------------------------------------------------------- + +EventContext::EventContext(std::vector& modules) + : ModuleContext(modules) { +} + +void EventContext::handle(Module* m) { + m->handleEvent(*this); +} + +} // namespace extra2d diff --git a/Extra2D/src/graphics/render_module.cpp b/Extra2D/src/graphics/render_module.cpp deleted file mode 100644 index 11b9ad3..0000000 --- a/Extra2D/src/graphics/render_module.cpp +++ /dev/null @@ -1,214 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -using json = nlohmann::json; - -namespace extra2d { - -static ModuleId s_renderModuleId = INVALID_MODULE_ID; - -ModuleId get_render_module_id() { - return s_renderModuleId; -} - -bool RenderModuleConfig::validate() const { - if (targetFPS < 1 || targetFPS > 240) { - return false; - } - - if (multisamples != 0 && multisamples != 2 && multisamples != 4 && - multisamples != 8 && multisamples != 16) { - return false; - } - - if (spriteBatchSize <= 0) { - return false; - } - - return true; -} - -void RenderModuleConfig::applyPlatformConstraints(PlatformType platform) { - switch (platform) { - case PlatformType::Switch: - if (multisamples > 4) { - multisamples = 4; - } - if (sRGBFramebuffer) { - sRGBFramebuffer = false; - } - if (targetFPS > 60) { - targetFPS = 60; - } - break; - default: - break; - } -} - -void RenderModuleConfig::resetToDefaults() { - backend = BackendType::OpenGL; - vsync = true; - targetFPS = 60; - multisamples = 0; - sRGBFramebuffer = false; - spriteBatchSize = 1000; -} - -bool RenderModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("backend")) { - std::string backendStr = j["backend"].get(); - if (backendStr == "opengl") { - backend = BackendType::OpenGL; - } - } - - if (j.contains("vsync")) { - vsync = j["vsync"].get(); - } - - if (j.contains("targetFPS")) { - targetFPS = j["targetFPS"].get(); - } - - if (j.contains("multisamples")) { - multisamples = j["multisamples"].get(); - } - - if (j.contains("sRGBFramebuffer")) { - sRGBFramebuffer = j["sRGBFramebuffer"].get(); - } - - if (j.contains("spriteBatchSize")) { - spriteBatchSize = j["spriteBatchSize"].get(); - } - - return true; - } catch (...) { - return false; - } -} - -bool RenderModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - j["backend"] = "opengl"; - j["vsync"] = vsync; - j["targetFPS"] = targetFPS; - j["multisamples"] = multisamples; - j["sRGBFramebuffer"] = sRGBFramebuffer; - j["spriteBatchSize"] = spriteBatchSize; - return true; - } catch (...) { - return false; - } -} - -RenderModuleInitializer::RenderModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , window_(nullptr) - , initialized_(false) { -} - -RenderModuleInitializer::~RenderModuleInitializer() { - if (initialized_) { - shutdown(); - } -} - -std::vector RenderModuleInitializer::getDependencies() const { - return {}; -} - -bool RenderModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - if (!config) return false; - - const RenderModuleConfig* renderConfig = dynamic_cast(config); - if (!renderConfig) return false; - - if (!renderConfig->validate()) return false; - - if (!window_) { - E2D_LOG_ERROR("Render module requires window to be set"); - return false; - } - - auto shaderFactory = std::make_shared(); - if (!ShaderManager::getInstance().init(shaderFactory, "extra2d")) { - E2D_LOG_WARN("Failed to initialize ShaderManager with default paths"); - } - - if (!ShaderManager::getInstance().loadBuiltinShaders()) { - E2D_LOG_WARN("Failed to load some builtin shaders"); - } - - renderer_ = RenderBackend::create(renderConfig->backend); - if (!renderer_) { - E2D_LOG_ERROR("Failed to create render backend"); - return false; - } - - if (!renderer_->init(window_)) { - E2D_LOG_ERROR("Failed to initialize renderer"); - renderer_.reset(); - return false; - } - - initialized_ = true; - E2D_LOG_INFO("Render module initialized"); - return true; -} - -void RenderModuleInitializer::shutdown() { - if (!initialized_) return; - - if (renderer_) { - renderer_->shutdown(); - renderer_.reset(); - } - - ShaderManager::getInstance().shutdown(); - - initialized_ = false; - E2D_LOG_INFO("Render module shutdown"); -} - -void register_render_module() { - if (s_renderModuleId != INVALID_MODULE_ID) return; - - s_renderModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_renderModuleId); - return initializer; - } - ); -} - -namespace { - struct RenderModuleAutoRegister { - RenderModuleAutoRegister() { - register_render_module(); - } - }; - - static RenderModuleAutoRegister s_autoRegister; -} - -} // namespace extra2d diff --git a/Extra2D/src/modules/config_module.cpp b/Extra2D/src/modules/config_module.cpp new file mode 100644 index 0000000..f17630e --- /dev/null +++ b/Extra2D/src/modules/config_module.cpp @@ -0,0 +1,53 @@ +#include +#include + +namespace extra2d { + +ConfigModule::ConfigModule() + : Module() { +} + +ConfigModule::~ConfigModule() { + if (isInitialized()) { + destroyModule(); + } +} + +void ConfigModule::setupModule() { + if (isInitialized()) { + return; + } + + if (!configPath_.empty()) { + if (!ConfigManager::instance().initialize(configPath_)) { + if (!ConfigManager::instance().initialize()) { + E2D_LOG_ERROR("Config module initialization failed"); + return; + } + } + } else { + if (!ConfigManager::instance().initialize()) { + E2D_LOG_ERROR("Config module initialization failed"); + return; + } + } + + if (!appConfig_.appName.empty()) { + ConfigManager::instance().setAppConfig(appConfig_); + } + + setInitialized(true); + E2D_LOG_INFO("Config module initialized"); +} + +void ConfigModule::destroyModule() { + if (!isInitialized()) { + return; + } + + E2D_LOG_INFO("Config module shutting down"); + ConfigManager::instance().shutdown(); + setInitialized(false); +} + +} // namespace extra2d diff --git a/Extra2D/src/modules/input_module.cpp b/Extra2D/src/modules/input_module.cpp new file mode 100644 index 0000000..13d7f60 --- /dev/null +++ b/Extra2D/src/modules/input_module.cpp @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#include "../platform/backends/sdl2/sdl2_input.h" + +namespace extra2d { + +InputModule::InputModule() + : Module() + , window_(nullptr) + , input_(nullptr) { +} + +InputModule::~InputModule() { + if (isInitialized()) { + destroyModule(); + } +} + +void InputModule::setupModule() { + if (isInitialized()) { + return; + } + + if (!window_) { + E2D_LOG_ERROR("Window not set - cannot initialize input module"); + return; + } + + input_ = window_->input(); + if (!input_) { + E2D_LOG_ERROR("Input interface not available from window"); + return; + } + + SDL2Input* sdl2Input = dynamic_cast(input_); + if (sdl2Input) { + auto eventService = ServiceLocator::instance().getService(); + if (eventService) { + sdl2Input->setEventCallback([eventService](const Event& event) { + Event mutableEvent = event; + eventService->dispatch(mutableEvent); + }); + E2D_LOG_INFO("Input events connected to EventService"); + } else { + E2D_LOG_WARN("EventService not available - input events will not be dispatched"); + } + } + + setInitialized(true); + E2D_LOG_INFO("Input module initialized"); + E2D_LOG_INFO(" Deadzone: {}", config_.deadzone); + E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity); + E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled"); +} + +void InputModule::destroyModule() { + if (!isInitialized()) { + return; + } + + E2D_LOG_INFO("Input module shutting down"); + + input_ = nullptr; + setInitialized(false); +} + +void InputModule::onUpdate(UpdateContext& ctx) { + if (!isInitialized() || !input_) { + ctx.next(); + return; + } + + input_->update(); + ctx.next(); +} + +} // namespace extra2d diff --git a/Extra2D/src/modules/logger_module.cpp b/Extra2D/src/modules/logger_module.cpp new file mode 100644 index 0000000..35bd9ea --- /dev/null +++ b/Extra2D/src/modules/logger_module.cpp @@ -0,0 +1,47 @@ +#include +#include + +namespace extra2d { + +LoggerModule::LoggerModule() + : Module() + , logLevel_(LogLevel::Info) + , consoleOutput_(true) + , fileOutput_(false) { +} + +LoggerModule::~LoggerModule() { + if (isInitialized()) { + destroyModule(); + } +} + +void LoggerModule::setupModule() { + if (isInitialized()) { + return; + } + + Logger::init(); + + Logger::setLevel(logLevel_); + Logger::setConsoleOutput(consoleOutput_); + + if (fileOutput_ && !logFilePath_.empty()) { + Logger::setFileOutput(logFilePath_); + } + + setInitialized(true); + E2D_LOG_INFO("Logger module initialized"); +} + +void LoggerModule::destroyModule() { + if (!isInitialized()) { + return; + } + + E2D_LOG_INFO("Logger module shutting down"); + Logger::shutdown(); + setInitialized(false); +} + +} // namespace extra2d diff --git a/Extra2D/src/modules/platform_module.cpp b/Extra2D/src/modules/platform_module.cpp new file mode 100644 index 0000000..102b3da --- /dev/null +++ b/Extra2D/src/modules/platform_module.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#ifdef __SWITCH__ +#include +#endif + +namespace extra2d { + +PlatformModule::PlatformModule() + : Module() + , targetPlatform_(PlatformType::Auto) + , resolvedPlatform_(PlatformType::Windows) { +} + +PlatformModule::~PlatformModule() { + if (isInitialized()) { + destroyModule(); + } +} + +void PlatformModule::setupModule() { + if (isInitialized()) { + return; + } + + resolvedPlatform_ = targetPlatform_; + if (resolvedPlatform_ == PlatformType::Auto) { +#ifdef __SWITCH__ + resolvedPlatform_ = PlatformType::Switch; +#else +#ifdef _WIN32 + resolvedPlatform_ = PlatformType::Windows; +#elif defined(__linux__) + resolvedPlatform_ = PlatformType::Linux; +#elif defined(__APPLE__) + resolvedPlatform_ = PlatformType::macOS; +#else + resolvedPlatform_ = PlatformType::Windows; +#endif +#endif + } + + platformConfig_ = createPlatformConfig(resolvedPlatform_); + if (!platformConfig_) { + E2D_LOG_ERROR("Failed to create platform config"); + return; + } + + if (resolvedPlatform_ == PlatformType::Switch) { + if (!initSwitch()) { + return; + } + } + + setInitialized(true); + E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_)); +} + +void PlatformModule::destroyModule() { + if (!isInitialized()) { + return; + } + + E2D_LOG_INFO("Platform module shutting down"); + + if (resolvedPlatform_ == PlatformType::Switch) { + shutdownSwitch(); + } + + platformConfig_.reset(); + setInitialized(false); +} + +bool PlatformModule::initSwitch() { +#ifdef __SWITCH__ + Result rc; + rc = romfsInit(); + if (R_SUCCEEDED(rc)) { + E2D_LOG_INFO("RomFS initialized successfully"); + } else { + E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc); + } + + rc = socketInitializeDefault(); + if (R_FAILED(rc)) { + E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available"); + } +#endif + return true; +} + +void PlatformModule::shutdownSwitch() { +#ifdef __SWITCH__ + romfsExit(); + socketExit(); +#endif +} + +} // namespace extra2d diff --git a/Extra2D/src/modules/render_module.cpp b/Extra2D/src/modules/render_module.cpp new file mode 100644 index 0000000..af25840 --- /dev/null +++ b/Extra2D/src/modules/render_module.cpp @@ -0,0 +1,98 @@ +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +RenderModule::RenderModule() + : Module() + , window_(nullptr) + , renderer_(nullptr) { +} + +RenderModule::~RenderModule() { + if (isInitialized()) { + destroyModule(); + } +} + +void RenderModule::setupModule() { + if (isInitialized()) { + return; + } + + if (!config_.validate()) { + E2D_LOG_ERROR("Invalid render config"); + return; + } + + if (!window_) { + E2D_LOG_ERROR("Render module requires window to be set"); + return; + } + + auto shaderFactory = std::make_shared(); + if (!ShaderManager::getInstance().init(shaderFactory, "extra2d")) { + E2D_LOG_WARN("Failed to initialize ShaderManager with default paths"); + } + + if (!ShaderManager::getInstance().loadBuiltinShaders()) { + E2D_LOG_WARN("Failed to load some builtin shaders"); + } + + renderer_ = RenderBackend::create(config_.backend); + if (!renderer_) { + E2D_LOG_ERROR("Failed to create render backend"); + return; + } + + if (!renderer_->init(window_)) { + E2D_LOG_ERROR("Failed to initialize renderer"); + renderer_.reset(); + return; + } + + setInitialized(true); + E2D_LOG_INFO("Render module initialized"); +} + +void RenderModule::destroyModule() { + if (!isInitialized()) { + return; + } + + if (renderer_) { + renderer_->shutdown(); + renderer_.reset(); + } + + ShaderManager::getInstance().shutdown(); + + setInitialized(false); + E2D_LOG_INFO("Render module shutdown"); +} + +void RenderModule::beforeRender(RenderContext& ctx) { + if (!isInitialized() || !renderer_) { + ctx.next(); + return; + } + + renderer_->beginFrame(Color(0.0f, 0.0f, 0.0f, 1.0f)); + ctx.next(); +} + +void RenderModule::afterRender(RenderContext& ctx) { + if (!isInitialized() || !window_) { + ctx.next(); + return; + } + + window_->swap(); + ctx.next(); +} + +} // namespace extra2d diff --git a/Extra2D/src/modules/window_module.cpp b/Extra2D/src/modules/window_module.cpp new file mode 100644 index 0000000..1852837 --- /dev/null +++ b/Extra2D/src/modules/window_module.cpp @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +#ifdef __SWITCH__ +#include +#endif + +namespace extra2d { + +WindowModule::WindowModule() + : Module() + , sdl2Initialized_(false) { +} + +WindowModule::~WindowModule() { + if (isInitialized()) { + destroyModule(); + } +} + +void WindowModule::setupModule() { + if (isInitialized()) { + return; + } + +#ifdef __SWITCH__ + windowConfig_.mode = WindowMode::Fullscreen; + windowConfig_.resizable = false; + windowConfig_.highDPI = false; + E2D_LOG_INFO("Switch platform: forcing fullscreen mode"); +#endif + + if (!initSDL2()) { + return; + } + + extern void initSDL2Backend(); + initSDL2Backend(); + + if (!BackendFactory::has("sdl2")) { + E2D_LOG_ERROR("SDL2 backend not registered!"); + shutdownSDL2(); + return; + } + + if (!createWindow(windowConfig_)) { + E2D_LOG_ERROR("Failed to create window"); + shutdownSDL2(); + return; + } + + setInitialized(true); + E2D_LOG_INFO("Window module initialized"); + E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height()); + E2D_LOG_INFO(" Backend: SDL2"); + E2D_LOG_INFO(" VSync: {}", windowConfig_.vsync); + E2D_LOG_INFO(" Fullscreen: {}", windowConfig_.isFullscreen()); +} + +void WindowModule::destroyModule() { + if (!isInitialized()) { + return; + } + + E2D_LOG_INFO("Window module shutting down"); + + if (window_) { + window_->destroy(); + window_.reset(); + } + + shutdownSDL2(); + + setInitialized(false); +} + +bool WindowModule::initSDL2() { + Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER; + +#ifdef __SWITCH__ + initFlags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER; +#endif + + if (SDL_Init(initFlags) != 0) { + E2D_LOG_ERROR("Failed to initialize SDL2: {}", SDL_GetError()); + return false; + } + + sdl2Initialized_ = true; + E2D_LOG_INFO("SDL2 initialized successfully"); + return true; +} + +void WindowModule::shutdownSDL2() { + if (!sdl2Initialized_) { + return; + } + + SDL_Quit(); + sdl2Initialized_ = false; + E2D_LOG_INFO("SDL2 shutdown"); +} + +bool WindowModule::createWindow(const WindowConfigData& config) { + window_ = BackendFactory::createWindow("sdl2"); + if (!window_) { + E2D_LOG_ERROR("Failed to create SDL2 window"); + return false; + } + + if (!window_->create(config)) { + E2D_LOG_ERROR("Failed to create window with specified config"); + return false; + } + + return true; +} + +} // namespace extra2d diff --git a/Extra2D/src/platform/input_module.cpp b/Extra2D/src/platform/input_module.cpp deleted file mode 100644 index 6babecc..0000000 --- a/Extra2D/src/platform/input_module.cpp +++ /dev/null @@ -1,213 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "backends/sdl2/sdl2_input.h" - -using json = nlohmann::json; - -namespace extra2d { - -static ModuleId s_inputModuleId = INVALID_MODULE_ID; - -ModuleId get_input_module_id() { - return s_inputModuleId; -} - -bool InputModuleConfig::validate() const { - return inputConfig.isDeadzoneValid(); -} - -void InputModuleConfig::applyPlatformConstraints(PlatformType platform) { -#ifdef __SWITCH__ - (void)platform; - inputConfig.enableVibration = true; - inputConfig.maxGamepads = 2; -#else - (void)platform; -#endif -} - -bool InputModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("enabled")) { - inputConfig.enabled = j["enabled"].get(); - } - if (j.contains("rawMouseInput")) { - inputConfig.rawMouseInput = j["rawMouseInput"].get(); - } - if (j.contains("mouseSensitivity")) { - inputConfig.mouseSensitivity = j["mouseSensitivity"].get(); - } - if (j.contains("invertMouseY")) { - inputConfig.invertMouseY = j["invertMouseY"].get(); - } - if (j.contains("invertMouseX")) { - inputConfig.invertMouseX = j["invertMouseX"].get(); - } - if (j.contains("deadzone")) { - inputConfig.deadzone = j["deadzone"].get(); - } - if (j.contains("triggerThreshold")) { - inputConfig.triggerThreshold = j["triggerThreshold"].get(); - } - if (j.contains("enableVibration")) { - inputConfig.enableVibration = j["enableVibration"].get(); - } - if (j.contains("maxGamepads")) { - inputConfig.maxGamepads = j["maxGamepads"].get(); - } - if (j.contains("autoConnectGamepads")) { - inputConfig.autoConnectGamepads = j["autoConnectGamepads"].get(); - } - if (j.contains("gamepadMappingFile")) { - inputConfig.gamepadMappingFile = j["gamepadMappingFile"].get(); - } - - return true; - } catch (...) { - return false; - } -} - -bool InputModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - - j["enabled"] = inputConfig.enabled; - j["rawMouseInput"] = inputConfig.rawMouseInput; - j["mouseSensitivity"] = inputConfig.mouseSensitivity; - j["invertMouseY"] = inputConfig.invertMouseY; - j["invertMouseX"] = inputConfig.invertMouseX; - j["deadzone"] = inputConfig.deadzone; - j["triggerThreshold"] = inputConfig.triggerThreshold; - j["enableVibration"] = inputConfig.enableVibration; - j["maxGamepads"] = inputConfig.maxGamepads; - j["autoConnectGamepads"] = inputConfig.autoConnectGamepads; - j["gamepadMappingFile"] = inputConfig.gamepadMappingFile; - - return true; - } catch (...) { - return false; - } -} - -InputModuleInitializer::InputModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , input_(nullptr) - , initialized_(false) { -} - -InputModuleInitializer::~InputModuleInitializer() { - if (initialized_) { - shutdown(); - } -} - -std::vector InputModuleInitializer::getDependencies() const { - return { get_window_module_id() }; -} - -bool InputModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - const InputModuleConfig* inputConfig = dynamic_cast(config); - if (!inputConfig) { - E2D_LOG_ERROR("Invalid input module config"); - return false; - } - - config_ = inputConfig->inputConfig; - - auto& registry = ModuleRegistry::instance(); - auto* windowInitializer = registry.getInitializer(get_window_module_id()); - if (!windowInitializer) { - E2D_LOG_ERROR("Window module not found - Input module depends on it"); - return false; - } - - auto* windowModule = static_cast(windowInitializer); - IWindow* window = windowModule->getWindow(); - if (!window) { - E2D_LOG_ERROR("Window not created - cannot get input interface"); - return false; - } - - input_ = window->input(); - if (!input_) { - E2D_LOG_ERROR("Input interface not available from window"); - return false; - } - - SDL2Input* sdl2Input = dynamic_cast(input_); - if (sdl2Input) { - auto eventService = ServiceLocator::instance().getService(); - if (eventService) { - sdl2Input->setEventCallback([eventService](const Event& event) { - Event mutableEvent = event; - eventService->dispatch(mutableEvent); - }); - E2D_LOG_INFO("Input events connected to EventService"); - } else { - E2D_LOG_WARN("EventService not available - input events will not be dispatched"); - } - } - - initialized_ = true; - E2D_LOG_INFO("Input module initialized"); - E2D_LOG_INFO(" Deadzone: {}", config_.deadzone); - E2D_LOG_INFO(" Mouse sensitivity: {}", config_.mouseSensitivity); - E2D_LOG_INFO(" Vibration: {}", config_.enableVibration ? "enabled" : "disabled"); - - return true; -} - -void InputModuleInitializer::shutdown() { - if (!initialized_) return; - - E2D_LOG_INFO("Input module shutting down"); - - input_ = nullptr; - initialized_ = false; -} - -void InputModuleInitializer::update() { - if (!initialized_ || !input_) return; - - input_->update(); -} - -void register_input_module() { - if (s_inputModuleId != INVALID_MODULE_ID) return; - - s_inputModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_inputModuleId); - return initializer; - } - ); -} - -namespace { - struct InputModuleAutoRegister { - InputModuleAutoRegister() { - register_input_module(); - } - }; - - static InputModuleAutoRegister s_autoRegister; -} - -} diff --git a/Extra2D/src/platform/platform_init_module.cpp b/Extra2D/src/platform/platform_init_module.cpp deleted file mode 100644 index 08efe8d..0000000 --- a/Extra2D/src/platform/platform_init_module.cpp +++ /dev/null @@ -1,168 +0,0 @@ -#include -#include -#include -#include -#include - -#ifdef __SWITCH__ -#include -#endif - -using json = nlohmann::json; - -namespace extra2d { - -static ModuleId s_platformModuleId = INVALID_MODULE_ID; - -ModuleId get_platform_module_id() { - return s_platformModuleId; -} - -bool PlatformModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("targetPlatform")) { - int platform = j["targetPlatform"].get(); - if (platform >= 0 && platform <= 5) { - targetPlatform = static_cast(platform); - } - } - - return true; - } catch (...) { - return false; - } -} - -bool PlatformModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - j["targetPlatform"] = static_cast(targetPlatform); - return true; - } catch (...) { - return false; - } -} - -PlatformModuleInitializer::PlatformModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , initialized_(false) - , targetPlatform_(PlatformType::Auto) - , resolvedPlatform_(PlatformType::Windows) { -} - -PlatformModuleInitializer::~PlatformModuleInitializer() { - if (initialized_) { - shutdown(); - } -} - -bool PlatformModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - const PlatformModuleConfig* platformConfig = dynamic_cast(config); - if (platformConfig) { - targetPlatform_ = platformConfig->targetPlatform; - } - - resolvedPlatform_ = targetPlatform_; - if (resolvedPlatform_ == PlatformType::Auto) { -#ifdef __SWITCH__ - resolvedPlatform_ = PlatformType::Switch; -#else -#ifdef _WIN32 - resolvedPlatform_ = PlatformType::Windows; -#elif defined(__linux__) - resolvedPlatform_ = PlatformType::Linux; -#elif defined(__APPLE__) - resolvedPlatform_ = PlatformType::macOS; -#else - resolvedPlatform_ = PlatformType::Windows; -#endif -#endif - } - - platformConfig_ = createPlatformConfig(resolvedPlatform_); - if (!platformConfig_) { - E2D_LOG_ERROR("Failed to create platform config"); - return false; - } - - if (resolvedPlatform_ == PlatformType::Switch) { - if (!initSwitch()) { - return false; - } - } - - initialized_ = true; - E2D_LOG_INFO("Platform module initialized ({})", getPlatformTypeName(resolvedPlatform_)); - return true; -} - -bool PlatformModuleInitializer::initSwitch() { -#ifdef __SWITCH__ - Result rc; - rc = romfsInit(); - if (R_SUCCEEDED(rc)) { - E2D_LOG_INFO("RomFS initialized successfully"); - } else { - E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem", rc); - } - - rc = socketInitializeDefault(); - if (R_FAILED(rc)) { - E2D_LOG_WARN("socketInitializeDefault failed, nxlink will not be available"); - } -#endif - return true; -} - -void PlatformModuleInitializer::shutdown() { - if (!initialized_) return; - - E2D_LOG_INFO("Platform module shutting down"); - - if (resolvedPlatform_ == PlatformType::Switch) { - shutdownSwitch(); - } - - platformConfig_.reset(); - initialized_ = false; -} - -void PlatformModuleInitializer::shutdownSwitch() { -#ifdef __SWITCH__ - romfsExit(); - socketExit(); -#endif -} - -void register_platform_module() { - if (s_platformModuleId != INVALID_MODULE_ID) return; - - s_platformModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_platformModuleId); - return initializer; - } - ); -} - -namespace { - struct PlatformModuleAutoRegister { - PlatformModuleAutoRegister() { - register_platform_module(); - } - }; - - static PlatformModuleAutoRegister s_autoRegister; -} - -} // namespace extra2d diff --git a/Extra2D/src/platform/window_module.cpp b/Extra2D/src/platform/window_module.cpp deleted file mode 100644 index 82b3400..0000000 --- a/Extra2D/src/platform/window_module.cpp +++ /dev/null @@ -1,258 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#ifdef __SWITCH__ -#include -#endif - -using json = nlohmann::json; - -namespace extra2d { - -static ModuleId s_windowModuleId = INVALID_MODULE_ID; - -ModuleId get_window_module_id() { - return s_windowModuleId; -} - -void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) { -#ifdef __SWITCH__ - (void)platform; - windowConfig.mode = WindowMode::Fullscreen; - windowConfig.resizable = false; - windowConfig.highDPI = false; - windowConfig.width = 1920; - windowConfig.height = 1080; -#else - (void)platform; -#endif -} - -bool WindowModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("title")) { - windowConfig.title = j["title"].get(); - } - if (j.contains("width")) { - windowConfig.width = j["width"].get(); - } - if (j.contains("height")) { - windowConfig.height = j["height"].get(); - } - if (j.contains("minWidth")) { - windowConfig.minWidth = j["minWidth"].get(); - } - if (j.contains("minHeight")) { - windowConfig.minHeight = j["minHeight"].get(); - } - if (j.contains("fullscreen")) { - windowConfig.mode = j["fullscreen"].get() ? WindowMode::Fullscreen : WindowMode::Windowed; - } - if (j.contains("mode")) { - std::string modeStr = j["mode"].get(); - if (modeStr == "fullscreen") { - windowConfig.mode = WindowMode::Fullscreen; - } else if (modeStr == "borderless") { - windowConfig.mode = WindowMode::Borderless; - } else { - windowConfig.mode = WindowMode::Windowed; - } - } - if (j.contains("vsync")) { - windowConfig.vsync = j["vsync"].get(); - } - if (j.contains("resizable")) { - windowConfig.resizable = j["resizable"].get(); - } - if (j.contains("highDPI")) { - windowConfig.highDPI = j["highDPI"].get(); - } - if (j.contains("multisamples")) { - windowConfig.multisamples = j["multisamples"].get(); - } - - return true; - } catch (...) { - return false; - } -} - -bool WindowModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - j["title"] = windowConfig.title; - j["width"] = windowConfig.width; - j["height"] = windowConfig.height; - j["minWidth"] = windowConfig.minWidth; - j["minHeight"] = windowConfig.minHeight; - - switch (windowConfig.mode) { - case WindowMode::Fullscreen: - j["mode"] = "fullscreen"; - break; - case WindowMode::Borderless: - j["mode"] = "borderless"; - break; - default: - j["mode"] = "windowed"; - break; - } - - j["vsync"] = windowConfig.vsync; - j["resizable"] = windowConfig.resizable; - j["highDPI"] = windowConfig.highDPI; - j["multisamples"] = windowConfig.multisamples; - return true; - } catch (...) { - return false; - } -} - -WindowModuleInitializer::WindowModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , initialized_(false) - , sdl2Initialized_(false) { -} - -WindowModuleInitializer::~WindowModuleInitializer() { - if (initialized_) { - shutdown(); - } -} - -bool WindowModuleInitializer::initSDL2() { - Uint32 initFlags = SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER; - -#ifdef __SWITCH__ - initFlags |= SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER; -#endif - - if (SDL_Init(initFlags) != 0) { - E2D_LOG_ERROR("Failed to initialize SDL2: {}", SDL_GetError()); - return false; - } - - sdl2Initialized_ = true; - E2D_LOG_INFO("SDL2 initialized successfully"); - return true; -} - -void WindowModuleInitializer::shutdownSDL2() { - if (!sdl2Initialized_) return; - - SDL_Quit(); - sdl2Initialized_ = false; - E2D_LOG_INFO("SDL2 shutdown"); -} - -bool WindowModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - const WindowModuleConfig* windowConfig = dynamic_cast(config); - if (!windowConfig) { - E2D_LOG_ERROR("Invalid window module config"); - return false; - } - - windowConfig_ = windowConfig->windowConfig; - -#ifdef __SWITCH__ - windowConfig_.mode = WindowMode::Fullscreen; - windowConfig_.resizable = false; - windowConfig_.highDPI = false; - E2D_LOG_INFO("Switch platform: forcing fullscreen mode"); -#endif - - if (!initSDL2()) { - return false; - } - - extern void initSDL2Backend(); - initSDL2Backend(); - - if (!BackendFactory::has("sdl2")) { - E2D_LOG_ERROR("SDL2 backend not registered!"); - shutdownSDL2(); - return false; - } - - if (!createWindow(windowConfig_)) { - E2D_LOG_ERROR("Failed to create window"); - shutdownSDL2(); - return false; - } - - initialized_ = true; - E2D_LOG_INFO("Window module initialized"); - E2D_LOG_INFO(" Window: {}x{}", window_->width(), window_->height()); - E2D_LOG_INFO(" Backend: SDL2"); - E2D_LOG_INFO(" VSync: {}", windowConfig_.vsync); - E2D_LOG_INFO(" Fullscreen: {}", windowConfig_.isFullscreen()); - - return true; -} - -bool WindowModuleInitializer::createWindow(const WindowConfigData& config) { - window_ = BackendFactory::createWindow("sdl2"); - if (!window_) { - E2D_LOG_ERROR("Failed to create SDL2 window"); - return false; - } - - if (!window_->create(config)) { - E2D_LOG_ERROR("Failed to create window with specified config"); - return false; - } - - return true; -} - -void WindowModuleInitializer::shutdown() { - if (!initialized_) return; - - E2D_LOG_INFO("Window module shutting down"); - - if (window_) { - window_->destroy(); - window_.reset(); - } - - shutdownSDL2(); - - initialized_ = false; -} - -void register_window_module() { - if (s_windowModuleId != INVALID_MODULE_ID) return; - - s_windowModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_windowModuleId); - return initializer; - } - ); -} - -namespace { - struct WindowModuleAutoRegister { - WindowModuleAutoRegister() { - register_window_module(); - } - }; - - static WindowModuleAutoRegister s_autoRegister; -} - -} diff --git a/Extra2D/src/scene/node.cpp b/Extra2D/src/scene/node.cpp index 2e5b06f..ff5d033 100644 --- a/Extra2D/src/scene/node.cpp +++ b/Extra2D/src/scene/node.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include namespace extra2d { @@ -650,4 +652,40 @@ void Node::collectRenderCommands(std::vector &commands, } } +// ============================================================================ +// 事件系统便捷方法 +// ============================================================================ + +ListenerId Node::addListener(EventType type, EventDispatcher::EventCallback callback) { + auto eventService = ServiceLocator::instance().getService(); + if (eventService) { + return eventService->addListener(type, std::move(callback)); + } + return eventDispatcher_.addListener(type, std::move(callback)); +} + +void Node::removeListener(ListenerId id) { + auto eventService = ServiceLocator::instance().getService(); + if (eventService) { + eventService->removeListener(id); + } + eventDispatcher_.removeListener(id); +} + +void Node::removeAllListeners(EventType type) { + auto eventService = ServiceLocator::instance().getService(); + if (eventService) { + eventService->removeAllListeners(type); + } + eventDispatcher_.removeAllListeners(type); +} + +void Node::removeAllListeners() { + auto eventService = ServiceLocator::instance().getService(); + if (eventService) { + eventService->removeAllListeners(); + } + eventDispatcher_.removeAllListeners(); +} + } // namespace extra2d diff --git a/Extra2D/src/utils/logger_module.cpp b/Extra2D/src/utils/logger_module.cpp deleted file mode 100644 index 01713fd..0000000 --- a/Extra2D/src/utils/logger_module.cpp +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include -#include - -using json = nlohmann::json; - -namespace extra2d { - -bool LoggerModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("logLevel")) { - int level = j["logLevel"].get(); - if (level >= 0 && level <= 6) { - logLevel = static_cast(level); - } - } - - if (j.contains("consoleOutput")) { - consoleOutput = j["consoleOutput"].get(); - } - - if (j.contains("fileOutput")) { - fileOutput = j["fileOutput"].get(); - } - - if (j.contains("logFilePath")) { - logFilePath = j["logFilePath"].get(); - } - - return true; - } catch (...) { - return false; - } -} - -bool LoggerModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - j["logLevel"] = static_cast(logLevel); - j["consoleOutput"] = consoleOutput; - j["fileOutput"] = fileOutput; - j["logFilePath"] = logFilePath; - return true; - } catch (...) { - return false; - } -} - -LoggerModuleInitializer::LoggerModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , initialized_(false) { -} - -LoggerModuleInitializer::~LoggerModuleInitializer() { - if (initialized_) { - shutdown(); - } -} - -bool LoggerModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; - - const LoggerModuleConfig* loggerConfig = dynamic_cast(config); - - Logger::init(); - - if (loggerConfig) { - Logger::setLevel(loggerConfig->logLevel); - Logger::setConsoleOutput(loggerConfig->consoleOutput); - if (loggerConfig->fileOutput && !loggerConfig->logFilePath.empty()) { - Logger::setFileOutput(loggerConfig->logFilePath); - } - } - - initialized_ = true; - E2D_LOG_INFO("Logger module initialized"); - return true; -} - -void LoggerModuleInitializer::shutdown() { - if (!initialized_) return; - - E2D_LOG_INFO("Logger module shutting down"); - Logger::shutdown(); - initialized_ = false; -} - -namespace { - static ModuleId s_loggerModuleId = INVALID_MODULE_ID; - - struct LoggerModuleRegistrar { - LoggerModuleRegistrar() { - s_loggerModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_loggerModuleId); - return initializer; - } - ); - } - }; - - static LoggerModuleRegistrar s_registrar; -} - -} // namespace extra2d diff --git a/README.md b/README.md index 1777d8e..cb3156f 100644 --- a/README.md +++ b/README.md @@ -39,12 +39,11 @@ - **跨平台支持**:Windows、Linux、macOS、Nintendo Switch - **模块化架构**:模块系统 + 服务系统,灵活可扩展 +- **显式注册**:通过 `Application::use()` 注册模块,参考 Kiwano 设计 - **场景图系统**:树形节点结构,支持变换继承 - **输入系统**:键盘、鼠标、手柄、触摸,事件驱动 - **渲染系统**:OpenGL ES 3.2,支持自定义着色器 - **视口适配**:多种适配模式,自动响应窗口大小变化 -- **音频系统**:高质量音频播放(规划中) -- **UI 系统**:完整的 UI 控件支持(规划中) --- @@ -59,18 +58,19 @@ flowchart TB subgraph Core["Core Layer (核心层)"] direction LR - MR[ModuleRegistry
模块注册表] + MOD[Module
模块基类] SL[ServiceLocator
服务定位器] end subgraph Modules["Modules (模块系统)"] direction TB subgraph CoreModules["Core Modules"] + LOGGER[Logger Module
日志系统] CONFIG[Config Module
配置管理] PLATFORM[Platform Module
平台检测] WINDOW[Window Module
窗口管理] end - subgraph OtherModules["Feature Modules"] + subgraph FeatureModules["Feature Modules"] INPUT[Input Module
输入处理] RENDER[Render Module
渲染系统] end @@ -106,9 +106,9 @@ flowchart TB INPUT_SYS[Input System
输入系统] end - APP --> MR + APP --> MOD APP --> SL - MR --> Modules + MOD --> Modules SL --> Services SCENE_SVC --> SCENE SCENE --> NODE @@ -127,11 +127,12 @@ flowchart TB | 模块 | 职责 | 优先级 | |-----|------|-------| -| Config | 配置管理 | Core (0) | -| Platform | 平台检测 | Core (0) | -| Window | 窗口管理 | Core (0) | -| Input | 输入处理 | Input (50) | -| Render | 渲染系统 | Graphics (100) | +| Logger | 日志系统 | -1 | +| Config | 配置管理 | 0 | +| Platform | 平台检测 | 10 | +| Window | 窗口管理 | 20 | +| Input | 输入处理 | 30 | +| Render | 渲染系统 | 40 | ### 服务系统 @@ -213,8 +214,10 @@ int main() { config.appName = "My Game"; config.appVersion = "1.0.0"; - // 初始化 + // 获取应用实例 Application& app = Application::get(); + + // 初始化 if (!app.init(config)) { return -1; } @@ -244,6 +247,46 @@ int main() { } ``` +### 创建自定义模块 + +```cpp +#include + +class MyModule : public extra2d::Module { +public: + const char* getName() const override { return "MyModule"; } + + int getPriority() const override { return 1000; } + + void setupModule() override { + // 初始化资源 + extra2d::E2D_LOG_INFO("MyModule initialized"); + } + + void destroyModule() override { + // 清理资源 + extra2d::E2D_LOG_INFO("MyModule destroyed"); + } + + void onUpdate(extra2d::UpdateContext& ctx) override { + // 更新逻辑 + ctx.next(); // 继续下一个模块 + } +}; + +// 在 main 中注册 +int main() { + auto& app = extra2d::Application::get(); + + MyModule myModule; + app.use(myModule); // 显式注册 + + app.init(); + app.run(); + return 0; +} +``` + ### 场景图示例 ```cpp @@ -300,11 +343,13 @@ eventService->addListener(EventType::GamepadButtonPressed, [](Event& e) { | 示例 | 说明 | |-----|------| | `demo_basic` | 基础示例:场景图、输入事件、视口适配 | +| `demo_hello_module` | 自定义模块示例:展示如何创建和注册模块 | 运行示例: ```bash xmake run demo_basic +xmake run demo_hello_module ``` --- @@ -339,46 +384,36 @@ Extra2D/ │ │ ├── KHR/ # KHR 平台头文件 │ │ ├── extra2d/ # 引擎公共头文件 │ │ │ ├── app/ # 应用程序 -│ │ │ ├── audio/ # 音频配置 │ │ │ ├── config/ # 配置系统 -│ │ │ ├── core/ # 核心类型 -│ │ │ ├── debug/ # 调试配置 +│ │ │ ├── core/ # 核心类型、模块基类 │ │ │ ├── event/ # 事件系统 │ │ │ ├── graphics/ # 图形渲染 -│ │ │ │ └── opengl/ # OpenGL 实现 │ │ │ ├── input/ # 输入配置 +│ │ │ ├── modules/ # 内置模块 │ │ │ ├── platform/ # 平台抽象 -│ │ │ ├── resource/ # 资源配置 │ │ │ ├── scene/ # 场景系统 +│ │ │ ├── script/ # 脚本系统 │ │ │ ├── services/ # 服务接口 │ │ │ └── utils/ # 工具库 │ │ ├── glad/ # OpenGL 加载器 │ │ └── stb/ # STB 单文件库 │ ├── shaders/ # 着色器文件 -│ │ ├── builtin/ # 内置着色器 -│ │ ├── common/ # 公共着色器代码 -│ │ └── effects/ # 特效着色器 │ └── src/ # 源文件 │ ├── app/ # 应用实现 -│ ├── config/ # 配置实现 │ ├── core/ # 核心实现 -│ ├── debug/ # 调试实现 │ ├── event/ # 事件实现 -│ ├── glad/ # GLAD 实现 │ ├── graphics/ # 图形实现 -│ │ └── opengl/ # OpenGL 实现 +│ ├── modules/ # 模块实现 │ ├── platform/ # 平台实现 -│ │ └── backends/ # 后端实现 -│ │ └── sdl2/ # SDL2 后端 -│ ├── resource/ # 资源实现 │ ├── scene/ # 场景实现 +│ ├── script/ # 脚本实现 │ ├── services/ # 服务实现 │ └── utils/ # 工具实现 ├── docs/ # 文档 ├── examples/ # 示例程序 -│ └── basic/ # 基础示例 +│ ├── basic/ # 基础示例 +│ └── hello_module/ # 自定义模块示例 └── xmake/ # 构建配置 - └── toolchains/ # 工具链配置 ``` --- diff --git a/docs/module_system.md b/docs/module_system.md index ef074dc..d3a2b79 100644 --- a/docs/module_system.md +++ b/docs/module_system.md @@ -2,13 +2,13 @@ ## 概述 -Extra2D 采用模块化架构设计,所有核心功能通过模块系统和服务系统管理。系统提供: +Extra2D 采用模块化架构设计(参考 Kiwano),所有核心功能通过模块系统和服务系统管理。系统提供: -- **统一的生命周期管理**:初始化、关闭、依赖处理 -- **优先级排序**:确保模块/服务按正确顺序初始化 -- **模块化配置**:每个模块独立管理自己的配置 +- **统一的生命周期管理**:初始化、更新、渲染、关闭 +- **优先级排序**:确保模块按正确顺序初始化 +- **显式注册**:通过 `Application::use()` 注册模块 +- **Context 模式**:使用上下文对象遍历模块链 - **依赖注入**:通过服务定位器解耦模块间依赖 -- **可扩展性**:新增模块无需修改引擎核心代码 ## 架构图 @@ -21,8 +21,8 @@ Extra2D 采用模块化架构设计,所有核心功能通过模块系统和服 ┌───────────────┴───────────────┐ ▼ ▼ ┌─────────────────────────────┐ ┌─────────────────────────────┐ -│ ModuleRegistry │ │ ServiceLocator │ -│ (模块注册表,管理平台级模块) │ │ (服务定位器,管理运行时服务) │ +│ Modules │ │ ServiceLocator │ +│ (模块列表,按优先级排序) │ │ (服务定位器,管理运行时服务) │ └─────────────────────────────┘ └─────────────────────────────┘ │ │ ┌─────┴─────┐ ┌───────┴───────┐ @@ -33,79 +33,101 @@ Extra2D 采用模块化架构设计,所有核心功能通过模块系统和服 └───────────┘ └───────────┘ └───────────┘ └───────────┘ ``` -## 模块化配置系统 +--- -### 设计原则 +## 模块基类 -Extra2D 采用**模块化配置系统**,遵循开闭原则: +### Module -- **AppConfig** 只包含应用级别配置(appName, appVersion, organization 等) -- **各模块配置** 由模块自己管理,实现 `IModuleConfig` 接口 -- **新增模块** 无需修改引擎核心代码 - -### 配置文件结构 - -``` -Extra2D/include/extra2d/ -├── config/ -│ ├── app_config.h # 应用级别配置 -│ ├── module_config.h # 模块配置接口 -│ └── config_manager.h # 配置管理器 -├── platform/ -│ ├── window_config.h # 窗口模块配置 -│ └── input_config.h # 输入模块配置 -├── graphics/ -│ └── render_config.h # 渲染模块配置 -├── audio/ -│ └── audio_config.h # 音频模块配置 -├── debug/ -│ └── debug_config.h # 调试模块配置 -└── resource/ - └── resource_config.h # 资源模块配置 -``` - -### AppConfig 结构 +所有模块只需继承 `Module` 基类,实现需要的生命周期方法: ```cpp -struct AppConfig { - std::string appName = "Extra2D App"; - std::string appVersion = "1.0.0"; - std::string organization = ""; - std::string configFile = "config.json"; - PlatformType targetPlatform = PlatformType::Auto; - - static AppConfig createDefault(); - bool validate() const; - void reset(); - void merge(const AppConfig& other); -}; -``` - -### 模块配置示例 - -```cpp -// window_config.h -struct WindowConfigData { - std::string title = "Extra2D Application"; - int width = 1280; - int height = 720; - WindowMode mode = WindowMode::Windowed; - bool vsync = true; - bool resizable = true; - // ... -}; - -// window_module.h -class WindowModuleConfig : public IModuleConfig { +class Module { public: - WindowConfigData windowConfig; - - ModuleInfo getModuleInfo() const override; - std::string getConfigSectionName() const override { return "window"; } - bool validate() const override; - void applyPlatformConstraints(PlatformType platform) override; - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; + virtual ~Module() = default; + + /** + * @brief 设置模块(初始化) + */ + virtual void setupModule() {} + + /** + * @brief 销毁模块 + */ + virtual void destroyModule() {} + + /** + * @brief 更新时 + */ + virtual void onUpdate(UpdateContext& ctx) { ctx.next(); } + + /** + * @brief 渲染前 + */ + virtual void beforeRender(RenderContext& ctx) { ctx.next(); } + + /** + * @brief 渲染时 + */ + virtual void onRender(RenderContext& ctx) { ctx.next(); } + + /** + * @brief 渲染后 + */ + virtual void afterRender(RenderContext& ctx) { ctx.next(); } + + /** + * @brief 事件处理 + */ + virtual void handleEvent(EventContext& ctx) { ctx.next(); } + + /** + * @brief 获取模块名称 + */ + virtual const char* getName() const = 0; + + /** + * @brief 获取模块优先级 + */ + virtual int getPriority() const { return 0; } +}; +``` + +### 模块上下文 + +用于遍历模块链,支持链式调用: + +```cpp +/** + * @brief 模块上下文基类 + */ +class ModuleContext { +public: + void next(); // 遍历下一个模块 + bool isDone() const; // 检查是否完成 +}; + +/** + * @brief 更新上下文 + */ +class UpdateContext : public ModuleContext { +public: + float getDeltaTime() const; // 获取帧间隔时间 +}; + +/** + * @brief 渲染上下文 + */ +class RenderContext : public ModuleContext { +public: + enum class Phase { Before, On, After }; + Phase getPhase() const; +}; + +/** + * @brief 事件上下文 + */ +class EventContext : public ModuleContext { }; ``` @@ -117,283 +139,161 @@ public: |-----|--------------|---------------| | 用途 | 平台级初始化 | 运行时功能 | | 生命周期 | Application 管理 | ServiceLocator 管理 | -| 配置管理 | 独立配置文件 | 无配置 | -| 依赖方式 | 通过 ModuleRegistry | 通过 ServiceLocator | +| 注册方式 | `app.use(module)` | `locator.registerService()` | | 可替换性 | 编译时确定 | 运行时可替换 | | 示例 | Window, Render, Input | Scene, Timer, Event, Camera | +--- + ## 模块优先级 模块按优先级从小到大初始化,关闭时逆序执行: -| 优先级值 | 枚举名称 | 用途 | 模块示例 | -|---------|---------|------|---------| -| 0 | `Core` | 核心模块,最先初始化 | Config, Platform, Window | -| 50 | `Input` | 输入系统 | Input | -| 100 | `Graphics` | 图形渲染 | Render | -| 200 | `Audio` | 音频系统 | Audio | -| 500 | `Resource` | 资源管理 | Resource | - ---- - -## 模块系统 - -### IModuleConfig - -模块配置接口,定义模块的元数据和配置: - -```cpp -class IModuleConfig { -public: - virtual ~IModuleConfig() = default; - - virtual ModuleInfo getModuleInfo() const = 0; - virtual std::string getConfigSectionName() const = 0; - virtual bool validate() const = 0; - virtual void resetToDefaults() = 0; - virtual bool loadFromJson(const void* jsonData) = 0; - virtual bool saveToJson(void* jsonData) const = 0; - virtual void applyPlatformConstraints(PlatformType platform) {} -}; -``` - -### IModuleInitializer - -模块初始化器接口,管理模块的生命周期: - -```cpp -class IModuleInitializer { -public: - virtual ~IModuleInitializer() = default; - - virtual ModuleId getModuleId() const = 0; - virtual ModulePriority getPriority() const = 0; - virtual std::vector getDependencies() const = 0; - - virtual bool initialize(const IModuleConfig* config) = 0; - virtual void shutdown() = 0; - virtual bool isInitialized() const = 0; -}; -``` - -### ModuleRegistry - -模块注册表,管理所有模块: - -```cpp -class ModuleRegistry { -public: - static ModuleRegistry& instance(); - - ModuleId registerModule( - UniquePtr config, - ModuleInitializerFactory factory - ); - - IModuleConfig* getModuleConfig(ModuleId id); - IModuleInitializer* getInitializer(ModuleId id); - std::vector getAllModules() const; - std::vector getInitializationOrder() const; -}; -``` +| 模块 | 优先级 | 说明 | +|------|--------|------| +| LoggerModule | -1 | 最先初始化 | +| ConfigModule | 0 | 配置加载 | +| PlatformModule | 10 | 平台初始化 | +| WindowModule | 20 | 窗口创建 | +| InputModule | 30 | 输入系统 | +| RenderModule | 40 | 渲染系统 | +| ScriptModule | 500 | 脚本系统 | +| 用户模块 | 1000+ | 用户自定义 | --- ## 创建新模块 -### 步骤 1:定义配置数据结构 +### 简单示例 ```cpp -// my_module_config.h -#pragma once +#include -#include - -namespace extra2d { - -struct MyModuleConfigData { - int someSetting = 42; - bool enabled = true; - std::string path = "default"; +class MyModule : public extra2d::Module { +public: + const char* getName() const override { return "MyModule"; } + + int getPriority() const override { return 1000; } + + void setupModule() override { + // 初始化资源 + extra2d::E2D_LOG_INFO("MyModule initialized"); + } + + void destroyModule() override { + // 清理资源 + extra2d::E2D_LOG_INFO("MyModule destroyed"); + } + + void onUpdate(extra2d::UpdateContext& ctx) override { + // 更新逻辑 + ctx.next(); // 继续下一个模块 + } }; - -} ``` -### 步骤 2:定义配置类 +### 注册模块 + +```cpp +int main() { + auto& app = extra2d::Application::get(); + + MyModule myModule; + app.use(myModule); // 显式注册 + + app.init(); + app.run(); + return 0; +} +``` + +### 带配置的模块 ```cpp // my_module.h #pragma once -#include -#include -#include "my_module_config.h" +#include +#include -namespace extra2d { - -class MyModuleConfig : public IModuleConfig { -public: - MyModuleConfigData config; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "MyModule"; - info.version = "1.0.0"; - info.priority = ModulePriority::Graphics; - info.enabled = true; - return info; - } - - std::string getConfigSectionName() const override { return "my_module"; } - bool validate() const override { return config.someSetting > 0; } - - void resetToDefaults() override { - config = MyModuleConfigData{}; - } - - void applyPlatformConstraints(PlatformType platform) override { -#ifdef __SWITCH__ - config.someSetting = 30; // Switch 平台优化 -#else - (void)platform; -#endif - } - - bool loadFromJson(const void* jsonData) override; - bool saveToJson(void* jsonData) const override; +struct MyModuleConfig { + std::string greeting = "Hello!"; + int repeatCount = 1; }; -} -``` - -### 步骤 3:定义初始化器 - -```cpp -// my_module.h (续) -class MyModuleInitializer : public IModuleInitializer { +class MyModule : public extra2d::Module { public: - MyModuleInitializer(); - ~MyModuleInitializer() override; + MyModule(); + ~MyModule() override; - ModuleId getModuleId() const override { return moduleId_; } - ModulePriority getPriority() const override { return ModulePriority::Graphics; } - std::vector getDependencies() const override; + const char* getName() const override { return "MyModule"; } + int getPriority() const override { return 1000; } - bool initialize(const IModuleConfig* config) override; - void shutdown() override; - bool isInitialized() const override { return initialized_; } + void setupModule() override; + void destroyModule() override; + void onUpdate(extra2d::UpdateContext& ctx) override; - void setModuleId(ModuleId id) { moduleId_ = id; } + void setConfig(const MyModuleConfig& config) { config_ = config; } + void sayHello() const; private: - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; - MyModuleConfigData config_; + MyModuleConfig config_; + float time_ = 0.0f; }; - -ModuleId get_my_module_id(); -void register_my_module(); ``` -### 步骤 4:实现模块 - ```cpp // my_module.cpp #include "my_module.h" -#include #include -#include -using json = nlohmann::json; +MyModule::MyModule() : Module() {} -namespace extra2d { - -static ModuleId s_myModuleId = INVALID_MODULE_ID; - -ModuleId get_my_module_id() { return s_myModuleId; } - -bool MyModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - try { - const json& j = *static_cast(jsonData); - if (j.contains("someSetting")) config.someSetting = j["someSetting"].get(); - if (j.contains("enabled")) config.enabled = j["enabled"].get(); - if (j.contains("path")) config.path = j["path"].get(); - return true; - } catch (...) { - return false; +MyModule::~MyModule() { + if (isInitialized()) { + destroyModule(); } } -bool MyModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - try { - json& j = *static_cast(jsonData); - j["someSetting"] = config.someSetting; - j["enabled"] = config.enabled; - j["path"] = config.path; - return true; - } catch (...) { - return false; - } +void MyModule::setupModule() { + if (isInitialized()) return; + + setInitialized(true); + E2D_LOG_INFO("MyModule initialized"); + E2D_LOG_INFO(" Greeting: {}", config_.greeting); + E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount); + + sayHello(); } -MyModuleInitializer::MyModuleInitializer() : moduleId_(INVALID_MODULE_ID), initialized_(false) {} - -MyModuleInitializer::~MyModuleInitializer() { if (initialized_) shutdown(); } - -std::vector MyModuleInitializer::getDependencies() const { - return {}; // 无依赖 -} - -bool MyModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) return true; +void MyModule::destroyModule() { + if (!isInitialized()) return; - const MyModuleConfig* cfg = dynamic_cast(config); - if (!cfg) { - E2D_LOG_ERROR("Invalid MyModule config"); - return false; - } - - config_ = cfg->config; - - // 执行初始化逻辑... - - initialized_ = true; - E2D_LOG_INFO("MyModule initialized with setting: {}", config_.someSetting); - return true; -} - -void MyModuleInitializer::shutdown() { - if (!initialized_) return; - // 执行清理逻辑... - initialized_ = false; E2D_LOG_INFO("MyModule shutdown"); + setInitialized(false); } -void register_my_module() { - if (s_myModuleId != INVALID_MODULE_ID) return; +void MyModule::onUpdate(extra2d::UpdateContext& ctx) { + if (!isInitialized()) { + ctx.next(); + return; + } - s_myModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_myModuleId); - return initializer; - } - ); + time_ += ctx.getDeltaTime(); + + if (time_ >= 5.0f) { + sayHello(); + time_ = 0.0f; + } + + ctx.next(); } -namespace { - struct MyModuleAutoRegister { - MyModuleAutoRegister() { register_my_module(); } - }; - static MyModuleAutoRegister s_autoRegister; +void MyModule::sayHello() const { + for (int i = 0; i < config_.repeatCount; ++i) { + E2D_LOG_INFO("[MyModule] {}", config_.greeting); + } } - -} ``` --- @@ -404,90 +304,79 @@ namespace { **职责**:管理 ConfigManager 和应用配置 -**配置**: +**优先级**:0 + ```cpp -AppConfig config; +extra2d::AppConfig config; config.appName = "My Application"; config.appVersion = "1.0.0"; ``` --- +### Logger 模块 + +**职责**:日志系统初始化 + +**优先级**:-1(最先初始化) + +```cpp +extra2d::LoggerModule loggerModule; +loggerModule.setLogLevel(extra2d::LogLevel::Debug); +loggerModule.setFileOutput("app.log"); +app.use(loggerModule); +``` + +--- + ### Platform 模块 **职责**:平台检测和平台特定初始化 +**优先级**:10 + **支持平台**: - Windows - Linux - macOS - Nintendo Switch -**平台能力查询**: -```cpp -auto* platformConfig = createPlatformConfig(); -const auto& caps = platformConfig->capabilities(); -if (caps.supportsGamepad) { - // 支持手柄 -} -``` - --- ### Window 模块 **职责**:窗口创建和管理 -**后端**:统一使用 SDL2,支持所有平台 +**优先级**:20 + +**后端**:统一使用 SDL2 -**配置**: ```cpp -WindowConfigData config; -config.title = "My App"; -config.width = 1280; -config.height = 720; -config.mode = WindowMode::Windowed; -config.vsync = true; +extra2d::WindowModule windowModule; +extra2d::WindowConfigData windowConfig; +windowConfig.title = "My App"; +windowConfig.width = 1280; +windowConfig.height = 720; +windowConfig.vsync = true; +windowModule.setWindowConfig(windowConfig); +app.use(windowModule); ``` -**平台约束**: -- Switch 平台自动强制全屏模式 - --- ### Input 模块 **职责**:输入设备管理(键盘、鼠标、手柄) -**配置**: +**优先级**:30 + ```cpp -InputConfigData config; -config.deadzone = 0.15f; -config.mouseSensitivity = 1.0f; -config.enableVibration = true; -``` - -**使用示例**: -```cpp -IInput* input = app.window().input(); - -// 键盘 -if (input->pressed(Key::Space)) { - // 空格键刚按下 -} - -// 鼠标 -if (input->down(Mouse::Left)) { - Vec2 pos = input->mouse(); -} - -// 手柄 -if (input->gamepad()) { - Vec2 stick = input->leftStick(); - if (input->pressed(Gamepad::A)) { - input->vibrate(0.5f, 0.5f); // 振动反馈 - } -} +extra2d::InputModule inputModule; +extra2d::InputConfigData inputConfig; +inputConfig.deadzone = 0.15f; +inputConfig.enableVibration = true; +inputModule.setInputConfig(inputConfig); +app.use(inputModule); ``` --- @@ -496,45 +385,15 @@ if (input->gamepad()) { **职责**:渲染器初始化和管理 -**配置**: +**优先级**:40 + ```cpp -RenderConfigData config; -config.backend = BackendType::OpenGL; -config.vsync = true; -config.targetFPS = 60; -config.multisamples = 4; -``` - ---- - -## 配置文件格式 - -配置使用 JSON 格式,每个模块有独立的配置节: - -```json -{ - "app": { - "name": "My Application", - "version": "1.0.0", - "organization": "MyCompany" - }, - "window": { - "title": "My Application", - "width": 1280, - "height": 720, - "mode": "windowed", - "vsync": true - }, - "render": { - "targetFPS": 60, - "multisamples": 4 - }, - "input": { - "deadzone": 0.15, - "mouseSensitivity": 1.0, - "enableVibration": true - } -} +extra2d::RenderModule renderModule; +extra2d::RenderModuleConfig renderConfig; +renderConfig.vsync = true; +renderConfig.multisamples = 4; +renderModule.setRenderConfig(renderConfig); +app.use(renderModule); ``` --- @@ -543,7 +402,7 @@ config.multisamples = 4; ### IService -服务接口基类,所有服务必须实现: +服务接口基类: ```cpp class IService { @@ -554,10 +413,6 @@ public: virtual bool initialize() = 0; virtual void shutdown() = 0; virtual void update(float deltaTime); - virtual bool isInitialized() const; - - ServiceState getState() const; - const std::string& getName() const; }; ``` @@ -565,18 +420,21 @@ public: | 服务 | 用途 | 优先级 | |-----|------|-------| -| SceneService | 场景管理 | 300 | -| TimerService | 计时器 | 200 | | EventService | 事件分发 | 100 | +| TimerService | 计时器 | 200 | +| SceneService | 场景管理 | 300 | | CameraService | 相机系统 | 400 | ### 使用服务 ```cpp +auto& app = extra2d::Application::get(); + // 获取服务 -auto sceneService = Application::get().scenes(); -auto timerService = Application::get().timers(); -auto eventService = Application::get().events(); +auto sceneService = app.scenes(); +auto timerService = app.timers(); +auto eventService = app.events(); +auto cameraService = app.camera(); // 使用场景服务 sceneService->pushScene(myScene); @@ -587,8 +445,8 @@ timerService->addTimer(1.0f, []() { }); // 使用事件服务 -eventService->addListener(EventType::KeyPressed, [](Event& e) { - auto& keyEvent = std::get(e.data); +eventService->addListener(extra2d::EventType::KeyPressed, [](extra2d::Event& e) { + auto& keyEvent = std::get(e.data); E2D_LOG_INFO("Key pressed: {}", keyEvent.keyCode); }); ``` @@ -626,177 +484,50 @@ enum class EventType { // 窗口 WindowResize, WindowClose, - // ... }; ``` ### 事件监听 ```cpp -auto eventService = Application::get().events(); +auto eventService = app.events(); // 监听键盘事件 eventService->addListener(EventType::KeyPressed, [](Event& e) { auto& key = std::get(e.data); - E2D_LOG_INFO("Key: {}, mods: {}", key.keyCode, key.mods); + if (key.keyCode == static_cast(Key::Escape)) { + Application::get().quit(); + } }); // 监听鼠标事件 eventService->addListener(EventType::MouseButtonPressed, [](Event& e) { auto& mouse = std::get(e.data); - E2D_LOG_INFO("Mouse button: {} at ({}, {})", - mouse.button, mouse.position.x, mouse.position.y); + E2D_LOG_INFO("Click at ({}, {})", mouse.position.x, mouse.position.y); }); ``` --- -## 平台支持 - -### 支持的平台 - -| 平台 | 窗口后端 | 图形 API | 特殊处理 | -|-----|---------|---------|---------| -| Windows | SDL2 | OpenGL ES 3.2 | - | -| Linux | SDL2 | OpenGL ES 3.2 | - | -| macOS | SDL2 | OpenGL ES 3.2 | - | -| Nintendo Switch | SDL2 | OpenGL ES 3.2 | romfs, 强制全屏 | - -### 平台检测 - -```cpp -PlatformType platform = PlatformDetector::detect(); -const char* name = getPlatformTypeName(platform); - -switch (platform) { - case PlatformType::Windows: // Windows 处理 - case PlatformType::Switch: // Switch 处理 - // ... -} -``` - -### 平台能力 - -```cpp -auto* config = createPlatformConfig(); -const auto& caps = config->capabilities(); - -if (caps.supportsWindowed) { /* 支持窗口模式 */ } -if (caps.supportsGamepad) { /* 支持手柄 */ } -if (caps.supportsTouch) { /* 支持触摸 */ } -``` - ---- - -## 最佳实践 - -### 1. 模块配置独立化 - -```cpp -// 好的做法:模块管理自己的配置 -class WindowModuleConfig : public IModuleConfig { - WindowConfigData windowConfig; // 模块内部配置 -}; - -// 不好的做法:所有配置放在 AppConfig -struct AppConfig { - WindowConfigData window; // 耦合度高 - RenderConfigData render; - // ... 新增模块需要修改 AppConfig -}; -``` - -### 2. 使用平台约束 - -```cpp -void WindowModuleConfig::applyPlatformConstraints(PlatformType platform) { -#ifdef __SWITCH__ - windowConfig.mode = WindowMode::Fullscreen; - windowConfig.resizable = false; -#else - (void)platform; -#endif -} -``` - -### 3. 模块自动注册 - -```cpp -namespace { - struct MyModuleAutoRegister { - MyModuleAutoRegister() { register_my_module(); } - }; - static MyModuleAutoRegister s_autoRegister; // 程序启动时自动注册 -} -``` - ---- - -## 调试 - -### 查看模块初始化顺序 - -```cpp -auto order = ModuleRegistry::instance().getInitializationOrder(); -for (ModuleId id : order) { - auto* config = ModuleRegistry::instance().getModuleConfig(id); - if (config) { - auto info = config->getModuleInfo(); - E2D_LOG_INFO("Module: {} (priority: {})", - info.name, static_cast(info.priority)); - } -} -``` - -### 查看服务状态 - -```cpp -auto services = ServiceLocator::instance().getAllServices(); -for (const auto& service : services) { - auto info = service->getServiceInfo(); - E2D_LOG_INFO("Service: {} (state: {})", - info.name, static_cast(info.state)); -} -``` - ---- - ## 场景图系统 -### 概述 - -Extra2D 使用场景图(Scene Graph)管理游戏对象。场景图是一个树形结构,每个节点可以包含子节点,形成层级关系。 - ### Node 基类 -所有场景对象的基类,提供变换、层级管理和渲染功能: - ```cpp class Node : public std::enable_shared_from_this { public: // 层级管理 void addChild(Ptr child); void removeChild(Ptr child); - void detach(); - void clearChildren(); - Ptr getParent() const; - const std::vector>& getChildren() const; - Ptr findChild(const std::string& name) const; // 变换属性 void setPos(const Vec2& pos); void setRotation(float degrees); void setScale(const Vec2& scale); - void setAnchor(const Vec2& anchor); - void setOpacity(float opacity); - void setVisible(bool visible); - void setZOrder(int zOrder); // 世界变换 Vec2 toWorld(const Vec2& localPos) const; - Vec2 toLocal(const Vec2& worldPos) const; - glm::mat4 getLocalTransform() const; glm::mat4 getWorldTransform() const; // 生命周期回调 @@ -807,112 +538,53 @@ public: }; ``` -### Scene 类 - -场景是场景图的根节点,管理相机和视口: - -```cpp -class Scene : public Node { -public: - // 场景属性 - void setBackgroundColor(const Color& color); - - // 摄像机 - void setCamera(Ptr camera); - Camera* getActiveCamera() const; - - // 视口 - void setViewportSize(float width, float height); - - // 渲染和更新 - void renderScene(RenderBackend& renderer); - void updateScene(float dt); - - // 静态创建 - static Ptr create(); -}; -``` - ### ShapeNode 形状节点 -用于绘制几何形状: - ```cpp // 创建形状节点 -auto rect = ShapeNode::createFilledRect(Rect(0, 0, 100, 100), Color(1.0f, 0.4f, 0.4f, 1.0f)); -auto circle = ShapeNode::createFilledCircle(Vec2(0, 0), 50, Color(0.4f, 0.4f, 1.0f, 1.0f)); +auto rect = ShapeNode::createFilledRect( + Rect(0, 0, 100, 100), + Color(1.0f, 0.4f, 0.4f, 1.0f) +); + +auto circle = ShapeNode::createFilledCircle( + Vec2(0, 0), 50, + Color(0.4f, 0.4f, 1.0f, 1.0f) +); + auto triangle = ShapeNode::createFilledTriangle( Vec2(0, -40), Vec2(-35, 30), Vec2(35, 30), Color(0.4f, 1.0f, 0.4f, 1.0f) ); -auto line = ShapeNode::createLine(Vec2(0, 0), Vec2(100, 100), Color(1.0f, 1.0f, 1.0f, 1.0f), 2.0f); -auto polygon = ShapeNode::createFilledPolygon( - {Vec2(0, -50), Vec2(50, 0), Vec2(0, 50), Vec2(-50, 0)}, - Color(1.0f, 0.4f, 1.0f, 1.0f) -); ``` ### 变换继承 -子节点继承父节点的变换: - ```cpp auto parent = makeShared(); parent->setPos(100, 100); -parent->setRotation(45); // 旋转 45 度 +parent->setRotation(45); auto child = makeShared(); -child->setPos(50, 0); // 相对于父节点的位置 +child->setPos(50, 0); // 相对于父节点 parent->addChild(child); -// child 的世界位置 = parent 的变换 * child 的本地位置 // child 会随 parent 一起旋转 ``` -### 渲染流程 - -``` -Application::render() - └── CameraService::getViewProjectionMatrix() // 设置视图投影矩阵 - └── SceneService::render() - └── Scene::renderContent() - └── Node::render() (递归) - └── pushTransform(localTransform) // 压入本地变换 - └── onDraw() // 绘制形状 - └── children::onRender() // 递归渲染子节点 - └── popTransform() // 弹出变换 -``` - --- ## 视口适配系统 -### 概述 - -视口适配系统确保游戏内容在不同分辨率和宽高比的屏幕上正确显示。 - ### ViewportAdapter -视口适配器,计算视口位置和缩放: - ```cpp -// 视口适配模式 enum class ViewportMode { AspectRatio, // 保持宽高比,可能有黑边 Stretch, // 拉伸填满整个窗口 Center, // 居中显示,不缩放 Custom // 自定义缩放和偏移 }; - -// 视口配置 -struct ViewportConfig { - float logicWidth = 1920.0f; - float logicHeight = 1080.0f; - ViewportMode mode = ViewportMode::AspectRatio; - Color letterboxColor = Colors::Black; // 黑边颜色 - float customScale = 1.0f; - Vec2 customOffset = Vec2::Zero(); -}; ``` ### 使用 CameraService 配置视口 @@ -920,268 +592,64 @@ struct ViewportConfig { ```cpp auto cameraService = app.camera(); if (cameraService) { - ViewportConfig vpConfig; - vpConfig.logicWidth = 1280.0f; // 逻辑分辨率宽度 - vpConfig.logicHeight = 720.0f; // 逻辑分辨率高度 - vpConfig.mode = ViewportMode::AspectRatio; // 保持宽高比 - vpConfig.letterboxColor = Color(0.0f, 0.0f, 0.0f, 1.0f); // 黑边颜色 + extra2d::ViewportConfig vpConfig; + vpConfig.logicWidth = 1280.0f; + vpConfig.logicHeight = 720.0f; + vpConfig.mode = extra2d::ViewportMode::AspectRatio; cameraService->setViewportConfig(vpConfig); cameraService->updateViewport(windowWidth, windowHeight); - cameraService->applyViewportAdapter(); } ``` -### 窗口大小变化处理 - -当窗口大小变化时,Application 会自动更新视口: - -```cpp -// Application 内部处理 -window_->onResize([this, cameraService](int width, int height) { - cameraService->updateViewport(width, height); - cameraService->applyViewportAdapter(); - - auto sceneService = ServiceLocator::instance().getService(); - if (sceneService) { - auto currentScene = sceneService->getCurrentScene(); - if (currentScene) { - currentScene->setViewportSize(width, height); - } - } -}); -``` - -### 适配模式对比 - -| 模式 | 描述 | 适用场景 | -|-----|------|---------| -| `AspectRatio` | 保持宽高比,可能有黑边 | 大多数游戏 | -| `Stretch` | 拉伸填满整个窗口 | 不在乎变形的简单游戏 | -| `Center` | 居中显示,不缩放 | 固定分辨率的像素游戏 | -| `Custom` | 自定义缩放和偏移 | 特殊需求 | - --- ## 示例 完整示例请参考: -- [examples/hello_module/](../../examples/hello_module/) - **Hello World 自定义模块示例** -- [examples/basic/main.cpp](../../examples/basic/main.cpp) - 基础示例(场景图、输入事件、视口适配) -- [Extra2D/src/platform/window_module.cpp](../../Extra2D/src/platform/window_module.cpp) - Window 模块实现 -- [Extra2D/src/platform/input_module.cpp](../../Extra2D/src/platform/input_module.cpp) - Input 模块实现 -- [Extra2D/src/graphics/render_module.cpp](../../Extra2D/src/graphics/render_module.cpp) - Render 模块实现 -- [Extra2D/src/scene/node.cpp](../../Extra2D/src/scene/node.cpp) - Node 实现 -- [Extra2D/src/scene/shape_node.cpp](../../Extra2D/src/scene/shape_node.cpp) - ShapeNode 实现 +- [examples/hello_module/](../../examples/hello_module/) - 自定义模块示例 +- [examples/basic/main.cpp](../../examples/basic/main.cpp) - 基础示例 --- -## Hello World 自定义模块示例 +## 最佳实践 -### 示例概述 - -`examples/hello_module/` 目录包含一个完整的自定义模块示例,展示如何: - -1. 定义模块配置数据结构 -2. 实现 `IModuleConfig` 接口 -3. 实现 `IModuleInitializer` 接口 -4. 使用自动注册机制 -5. 支持 JSON 配置 - -### 文件结构 - -``` -examples/hello_module/ -├── hello_module.h # 模块头文件(配置类 + 初始化器类) -├── hello_module.cpp # 模块实现 -├── main.cpp # 示例入口 -└── config.json # 配置文件示例 -``` - -### 核心代码解析 - -#### 1. 配置数据结构 +### 1. 模块优先级 ```cpp -struct HelloModuleConfigData { - std::string greeting = "Hello, Extra2D!"; - int repeatCount = 1; - bool enableLogging = true; +// 核心模块使用低优先级 +class LoggerModule : public Module { + int getPriority() const override { return -1; } +}; + +// 用户模块使用高优先级 +class MyModule : public Module { + int getPriority() const override { return 1000; } }; ``` -#### 2. 配置类实现 +### 2. 链式调用 ```cpp -class HelloModuleConfig : public IModuleConfig { -public: - HelloModuleConfigData config; - - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.name = "HelloModule"; - info.version = "1.0.0"; - info.priority = ModulePriority::User; // 用户自定义模块 - return info; - } - - std::string getConfigSectionName() const override { - return "hello"; // 对应 config.json 中的 "hello" 节 - } - - bool validate() const override { - return !config.greeting.empty() && config.repeatCount > 0; - } -}; -``` - -#### 3. 初始化器实现 - -```cpp -class HelloModuleInitializer : public IModuleInitializer { -public: - bool initialize(const IModuleConfig* config) override { - const HelloModuleConfig* cfg = dynamic_cast(config); - if (!cfg || !cfg->validate()) { - return false; - } - - config_ = cfg->config; - initialized_ = true; - - // 执行模块初始化逻辑 - E2D_LOG_INFO("HelloModule initialized: {}", config_.greeting); - return true; - } - - void shutdown() override { - E2D_LOG_INFO("HelloModule shutdown"); - initialized_ = false; - } -}; -``` - -#### 4. 自动注册机制 - -```cpp -namespace { - struct HelloModuleAutoRegister { - HelloModuleAutoRegister() { - register_hello_module(); - } - }; +void onUpdate(UpdateContext& ctx) override { + // 执行更新逻辑 + doSomething(); - static HelloModuleAutoRegister s_autoRegister; // 程序启动时自动执行 + // 继续下一个模块 + ctx.next(); } ``` -### 配置文件示例 - -```json -{ - "hello": { - "greeting": "Hello from custom module!", - "repeatCount": 3, - "enableLogging": true - } -} -``` - -### 运行示例 - -```bash -xmake run demo_hello_module -``` - -### 预期输出 - -``` -[INFO] HelloModule initialized -[INFO] Greeting: Hello, Extra2D! -[INFO] Repeat Count: 1 -[INFO] Logging Enabled: true -[INFO] [HelloModule] Hello, Extra2D! -[INFO] HelloScene entered -[INFO] [HelloModule] Hello, Extra2D! # 场景 onEnter() 调用 -[INFO] Scene calling HelloModule from onUpdate... -[INFO] [HelloModule] Hello, Extra2D! # 场景每5秒调用 -[INFO] HelloModule shutdown - Goodbye! -``` - -### 在场景中使用模块 - -模块初始化后,可以在场景中通过 `ModuleRegistry` 获取模块实例并调用其功能: +### 3. 检查初始化状态 ```cpp -class HelloScene : public Scene { -public: - void onEnter() override { - Scene::onEnter(); - - // 获取模块初始化器 - ModuleId helloId = get_hello_module_id(); - auto* initializer = ModuleRegistry::instance().getInitializer(helloId); - - if (initializer) { - // 转换为具体类型 - auto* helloInit = dynamic_cast(initializer); - if (helloInit) { - // 调用模块功能 - helloInit->sayHello(); - } - } +void onUpdate(UpdateContext& ctx) override { + if (!isInitialized()) { + ctx.next(); + return; } - - void onUpdate(float dt) override { - Scene::onUpdate(dt); - - time_ += dt; - - // 每5秒调用一次模块功能 - if (time_ >= 5.0f) { - ModuleId helloId = get_hello_module_id(); - auto* initializer = ModuleRegistry::instance().getInitializer(helloId); - if (initializer) { - auto* helloInit = dynamic_cast(initializer); - if (helloInit) { - helloInit->sayHello(); - } - } - time_ = 0.0f; - } - } - -private: - float time_ = 0.0f; -}; -``` - -### 模块使用流程总结 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 1. 程序启动 │ -│ └── 静态变量 HelloModuleAutoRegister 自动注册模块 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 2. Application::init() │ -│ └── 遍历 ModuleRegistry 中所有已注册模块 │ -│ └── 按优先级顺序调用 initialize() │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 3. 场景/其他代码使用模块 │ -│ └── ModuleRegistry::getInitializer(moduleId) │ -│ └── dynamic_cast 转换为具体类型 │ -│ └── 调用模块方法 │ -└─────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────┐ -│ 4. Application::shutdown() │ -│ └── 按逆序调用所有模块的 shutdown() │ -└─────────────────────────────────────────────────────────────┘ + + // 执行更新逻辑 + ctx.next(); +} ``` diff --git a/examples/hello_module/hello_module.cpp b/examples/hello_module/hello_module.cpp index 50456da..a85c669 100644 --- a/examples/hello_module/hello_module.cpp +++ b/examples/hello_module/hello_module.cpp @@ -1,183 +1,79 @@ #include "hello_module.h" -#include #include -#include - -using json = nlohmann::json; namespace extra2d { -static ModuleId s_helloModuleId = INVALID_MODULE_ID; - -/** - * @brief 获取Hello模块标识符 - */ -ModuleId get_hello_module_id() { - return s_helloModuleId; +HelloModule::HelloModule() + : Module() + , config_() + , time_(0.0f) { } -/** - * @brief 从JSON加载配置 - */ -bool HelloModuleConfig::loadFromJson(const void* jsonData) { - if (!jsonData) return false; - - try { - const json& j = *static_cast(jsonData); - - if (j.contains("greeting")) { - config.greeting = j["greeting"].get(); - } - if (j.contains("repeatCount")) { - config.repeatCount = j["repeatCount"].get(); - } - if (j.contains("enableLogging")) { - config.enableLogging = j["enableLogging"].get(); - } - - return true; - } catch (...) { - return false; +HelloModule::~HelloModule() { + if (isInitialized()) { + destroyModule(); } } -/** - * @brief 保存配置到JSON - */ -bool HelloModuleConfig::saveToJson(void* jsonData) const { - if (!jsonData) return false; - - try { - json& j = *static_cast(jsonData); - j["greeting"] = config.greeting; - j["repeatCount"] = config.repeatCount; - j["enableLogging"] = config.enableLogging; - return true; - } catch (...) { - return false; +void HelloModule::setupModule() { + if (isInitialized()) { + return; } -} -/** - * @brief 构造函数 - */ -HelloModuleInitializer::HelloModuleInitializer() - : moduleId_(INVALID_MODULE_ID) - , initialized_(false) { -} + if (config_.greeting.empty()) { + config_.greeting = "Hello, Extra2D!"; + } -/** - * @brief 析构函数 - */ -HelloModuleInitializer::~HelloModuleInitializer() { - if (initialized_) { - shutdown(); + if (config_.repeatCount <= 0) { + config_.repeatCount = 1; } -} -/** - * @brief 获取模块依赖列表 - */ -std::vector HelloModuleInitializer::getDependencies() const { - return {}; -} - -/** - * @brief 初始化模块 - */ -bool HelloModuleInitializer::initialize(const IModuleConfig* config) { - if (initialized_) { - E2D_LOG_WARN("HelloModule already initialized"); - return true; - } - - if (!config) { - E2D_LOG_ERROR("HelloModule config is null"); - return false; - } - - const HelloModuleConfig* helloConfig = dynamic_cast(config); - if (!helloConfig) { - E2D_LOG_ERROR("Invalid HelloModule config type"); - return false; - } - - if (!helloConfig->validate()) { - E2D_LOG_ERROR("HelloModule config validation failed"); - return false; - } - - config_ = helloConfig->config; - - initialized_ = true; + setInitialized(true); E2D_LOG_INFO("HelloModule initialized"); E2D_LOG_INFO(" Greeting: {}", config_.greeting); E2D_LOG_INFO(" Repeat Count: {}", config_.repeatCount); E2D_LOG_INFO(" Logging Enabled: {}", config_.enableLogging); - + sayHello(); - - return true; } -/** - * @brief 关闭模块 - */ -void HelloModuleInitializer::shutdown() { - if (!initialized_) return; - +void HelloModule::destroyModule() { + if (!isInitialized()) { + return; + } + if (config_.enableLogging) { E2D_LOG_INFO("HelloModule shutdown - Goodbye!"); } - - initialized_ = false; + + setInitialized(false); } -/** - * @brief 执行问候操作 - */ -void HelloModuleInitializer::sayHello() const { - if (!config_.enableLogging) return; - +void HelloModule::onUpdate(UpdateContext& ctx) { + if (!isInitialized()) { + ctx.next(); + return; + } + + time_ += ctx.getDeltaTime(); + + if (time_ >= 5.0f) { + sayHello(); + time_ = 0.0f; + } + + ctx.next(); +} + +void HelloModule::sayHello() const { + if (!config_.enableLogging) { + return; + } + for (int i = 0; i < config_.repeatCount; ++i) { E2D_LOG_INFO("[HelloModule] {}", config_.greeting); } } -/** - * @brief 注册Hello模块 - */ -void register_hello_module() { - if (s_helloModuleId != INVALID_MODULE_ID) { - E2D_LOG_WARN("HelloModule already registered"); - return; - } - - s_helloModuleId = ModuleRegistry::instance().registerModule( - makeUnique(), - []() -> UniquePtr { - auto initializer = makeUnique(); - initializer->setModuleId(s_helloModuleId); - return initializer; - } - ); - - E2D_LOG_DEBUG("HelloModule registered with id: {}", s_helloModuleId); -} - -namespace { - /** - * @brief 自动注册器 - * 在程序启动时自动注册模块 - */ - struct HelloModuleAutoRegister { - HelloModuleAutoRegister() { - register_hello_module(); - } - }; - - static HelloModuleAutoRegister s_autoRegister; -} - } // namespace extra2d diff --git a/examples/hello_module/hello_module.h b/examples/hello_module/hello_module.h index 9a75f15..797e8df 100644 --- a/examples/hello_module/hello_module.h +++ b/examples/hello_module/hello_module.h @@ -1,7 +1,6 @@ #pragma once -#include -#include +#include #include namespace extra2d { @@ -16,113 +15,54 @@ struct HelloModuleConfigData { }; /** - * @brief Hello模块配置类 + * @brief Hello模块 * * 这是一个简单的自定义模块示例,展示如何: - * 1. 定义模块配置数据结构 - * 2. 实现IModuleConfig接口 - * 3. 支持JSON配置加载/保存 + * 1. 继承 Module 基类 + * 2. 实现生命周期方法 + * 3. 使用 Application::use() 注册模块 */ -class HelloModuleConfig : public IModuleConfig { +class HelloModule : public Module { public: - HelloModuleConfigData config; + /** + * @brief 构造函数 + */ + HelloModule(); /** - * @brief 获取模块信息 + * @brief 析构函数 */ - ModuleInfo getModuleInfo() const override { - ModuleInfo info; - info.id = 0; - info.name = "HelloModule"; - info.version = "1.0.0"; - info.priority = ModulePriority::User; - info.enabled = true; - return info; - } + ~HelloModule() override; /** - * @brief 获取配置节名称 + * @brief 获取模块名称 */ - std::string getConfigSectionName() const override { - return "hello"; - } - - /** - * @brief 验证配置有效性 - */ - bool validate() const override { - return !config.greeting.empty() && config.repeatCount > 0; - } - - /** - * @brief 重置为默认配置 - */ - void resetToDefaults() override { - config = HelloModuleConfigData{}; - } - - /** - * @brief 应用平台约束 - */ - void applyPlatformConstraints(PlatformType platform) override { - (void)platform; - } - - /** - * @brief 从JSON加载配置 - */ - bool loadFromJson(const void* jsonData) override; - - /** - * @brief 保存配置到JSON - */ - bool saveToJson(void* jsonData) const override; -}; - -/** - * @brief Hello模块初始化器 - * - * 负责模块的生命周期管理 - */ -class HelloModuleInitializer : public IModuleInitializer { -public: - HelloModuleInitializer(); - ~HelloModuleInitializer() override; - - /** - * @brief 获取模块标识符 - */ - ModuleId getModuleId() const override { return moduleId_; } + const char* getName() const override { return "HelloModule"; } /** * @brief 获取模块优先级 */ - ModulePriority getPriority() const override { return ModulePriority::User; } + int getPriority() const override { return 1000; } /** - * @brief 获取模块依赖列表 + * @brief 设置模块 */ - std::vector getDependencies() const override; + void setupModule() override; /** - * @brief 初始化模块 + * @brief 销毁模块 */ - bool initialize(const IModuleConfig* config) override; + void destroyModule() override; /** - * @brief 关闭模块 + * @brief 更新时 */ - void shutdown() override; + void onUpdate(UpdateContext& ctx) override; /** - * @brief 检查是否已初始化 + * @brief 设置配置 */ - bool isInitialized() const override { return initialized_; } - - /** - * @brief 设置模块标识符 - */ - void setModuleId(ModuleId id) { moduleId_ = id; } + void setConfig(const HelloModuleConfigData& config) { config_ = config; } /** * @brief 执行问候操作 @@ -130,19 +70,8 @@ public: void sayHello() const; private: - ModuleId moduleId_ = INVALID_MODULE_ID; - bool initialized_ = false; HelloModuleConfigData config_; + float time_ = 0.0f; }; -/** - * @brief 获取Hello模块标识符 - */ -ModuleId get_hello_module_id(); - -/** - * @brief 注册Hello模块 - */ -void register_hello_module(); - } // namespace extra2d diff --git a/examples/hello_module/main.cpp b/examples/hello_module/main.cpp index 738e20d..e2d91e0 100644 --- a/examples/hello_module/main.cpp +++ b/examples/hello_module/main.cpp @@ -1,6 +1,5 @@ #include "hello_module.h" #include -#include #include #include #include @@ -21,54 +20,21 @@ public: E2D_LOG_INFO("HelloScene entered"); setBackgroundColor(Color(0.1f, 0.1f, 0.2f, 1.0f)); - - ModuleId helloId = get_hello_module_id(); - auto *initializer = ModuleRegistry::instance().getInitializer(helloId); - if (initializer) { - auto *helloInit = dynamic_cast(initializer); - if (helloInit) { - E2D_LOG_INFO("Scene calling HelloModule from onEnter..."); - helloInit->sayHello(); - } - } - } - - void onUpdate(float dt) override { - Scene::onUpdate(dt); - - time_ += dt; - - if (time_ >= 5.0f) { - ModuleId helloId = get_hello_module_id(); - auto *initializer = ModuleRegistry::instance().getInitializer(helloId); - if (initializer) { - auto *helloInit = dynamic_cast(initializer); - if (helloInit) { - E2D_LOG_INFO("Scene calling HelloModule from onUpdate..."); - helloInit->sayHello(); - } - } - time_ = 0.0f; - } } private: - float time_ = 0.0f; }; /** * @brief 应用程序入口 */ int main(int argc, char *argv[]) { - (void)argc; - (void)argv; - - E2D_LOG_INFO("=== Hello Module Example ==="); - E2D_LOG_INFO("This example demonstrates how to create a custom module"); - E2D_LOG_INFO(""); Application &app = Application::get(); + HelloModule helloModule; + app.use(helloModule); + AppConfig appConfig; appConfig.appName = "HelloModule Example"; appConfig.appVersion = "1.0.0"; @@ -78,24 +44,12 @@ int main(int argc, char *argv[]) { return 1; } - E2D_LOG_INFO(""); - E2D_LOG_INFO("Application initialized successfully"); - E2D_LOG_INFO("HelloModule should have been auto-registered and initialized"); - E2D_LOG_INFO(""); - auto scene = HelloScene::create(); app.enterScene(scene); - E2D_LOG_INFO("Starting main loop..."); - E2D_LOG_INFO("Press ESC or close window to exit"); - E2D_LOG_INFO(""); - app.run(); - E2D_LOG_INFO("Application shutting down..."); - app.shutdown(); - E2D_LOG_INFO("Application shutdown complete"); return 0; }